🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
OCL Editor, system prototyper and ViewModel
This page was created by Alexandra on 2016-12-11. Last edited by Wikiadmin on 2026-07-29.

You can use MDriven Designer to define a model, validate its OCL and action-language expressions, run the model in the system prototyper, and inspect the resulting objects and ViewModel UI; this walkthrough is for modelers learning that workflow.

What you will build

This walkthrough uses a House class to show how model definitions become executable behavior. You will:

  • navigate a class and its related types in a generated diagram;
  • define a state machine for the lifecycle of a house;
  • add a guarded trigger and an ordinary method;
  • validate expressions in the OCL Editor;
  • start the model with the system prototyper and use the debugger to create and inspect objects; and
  • render a ViewModel-based UI and add an action that creates related data.

The model is expressed in UML. In the MDriven architecture, the model can contain classes, associations, derived attributes, OCL expressions, state machines, ViewModels, declarative UI definitions, and actions. See Documentation:MDriven Architecture for the wider architecture.

Navigate the generated class diagram

MDriven Designer can show an automatically generated view of a class, its attributes, and its associations. Use this view to inspect the model and navigate to connected classes.

  1. Double-click beside a class name and choose the class name to enter edit mode when you need to rename it.
  2. Select a class, an attribute, or an association to show its properties in the Property Inspector.
  3. Select a class in the circle around the centre class to navigate to that class.
  4. Select an attribute to inspect its properties.

An attribute should have a type. In the generated display, a ? after a type uses C# notation to show that the value may be null. In MDriven Designer, this is controlled by the attribute's Allow NULL property.

Attribute setting Generated type display Meaning
Allow NULL = True String? The attribute may have a null value.
Allow NULL = False String The attribute is not shown as nullable.

For example, a nullable Address string appears as String?. This distinction matters when you write a guard that tests whether an address has been supplied.

Model the House lifecycle with a state machine

A state machine describes the permitted lifecycle of an object. In this example, the state attribute for House is named State; it is identified with an S icon and is configured from the state diagram and Property Inspector.

Create the following lifecycle:

  1. Open the state diagram for House.
  2. Create an initial state and connect it to Plan.
  3. Add the top-level states Plan, Construction, Maintenance, and Demolition.
  4. Add a region inside Construction when construction needs its own internal lifecycle.
  5. In that region, add GroundWork as the initial state and add Building.
  6. Add the triggers shown below to move between states.
From state Trigger To state Notes
Initial None Plan A newly created House reaches Plan when no guard prevents the transition.
Plan StartConstruction Construction Add a guard so construction cannot start without an address.
GroundWork StartBuilding Building This transition is within the Construction region.
Construction ConstructionDone Maintenance Leaves the construction phase.
Maintenance Demolish Demolition Ends the lifecycle in this example.

A trigger created for a state transition is also added as a method on House. Trigger methods are identified by the trigger symbol; an ordinary method does not have that symbol.

Guard the StartConstruction trigger

A guard is a Boolean rule that must be true before a transition can occur. The first version of the rule can test whether the address is non-null:

self.Address.notnull

When the address is a string, non-null is not always sufficient. An empty string is not null, so a House can have an address value that is present but blank. Test strings for both null and empty values instead. The walkthrough uses the C# IsNullOrEmpty operator and negates its Boolean result.

After changing a model rule while the debugger is open, re-read the model before testing the changed rule. The running debugger otherwise continues to use the model that it loaded when it started.

Define and validate method logic

Use the method signature to define parameters and a return type, then use the Action Editor Body to implement the method. MDriven uses OCL expressions throughout the model. In a method body, the expression is action language: an OCL-based language that permits side effects such as changing data.

For example, define an ordinary method on House with this signature:

Method1(param1:integer, param2:integer):String

The expression below adds two integer parameters:

param1 + param2

The editor reports a type error because the expression returns an integer (System.Int32) while the method declares String as its return type. You can convert the result:

(param1 + param2).asstring

Alternatively, change the method return type in the Property Inspector to an integer. The editor then shows the actual and required return types as System.Int32.

The editor also identifies unknown names. For example, this expression is invalid because param2x is not a defined parameter:

param1 + param2x

Save the model after correcting errors. The model error list identifies the affected method and the unrecognized name. When you return to the class diagram, the trigger methods and Method1 appear on House.

Run the model in the system prototyper

The system prototyper starts a system based on your model. It is useful for creating objects, checking state behavior, and opening a debugger without first building a complete application.

  1. Select Play. The System prototyper dialog opens.
  2. In step 1, select persistence. Persistence is where data is stored.
  3. Select None when you want an in-memory run for this walkthrough. There is no persistence configuration in step 2 for this choice.
  4. In step 3, select Start System.
  5. Choose New Debugger to open the execute-and-debug window.

The prototyper can be closed after the debugger opens; the debugger remains available. The MDriven architecture also describes the Prototyper as a way to start a model, create objects, and view AutoForms per class. See Documentation:MDriven Architecture.

Query and change data in the debugger

The debugger accepts expressions in the OCL Editor. OCL queries do not have side effects: they read data but do not create or change it. Creating an object is a side effect, so it must be evaluated as action language.

Query all House instances

Use this OCL expression to retrieve all House objects:

House.allinstances

At the start of an empty prototype, the result list is empty. The result view shows the attributes defined on House for any objects returned.

Choose the expression language

Prefix a debugger row to state how it should be evaluated. The debugger supports ocl:, action:, and oclps:; OCL is the default.

Prefix Use Example
ocl: Read-only OCL query. ocl: House.allinstances
action: Action-language expression that can change data. action: House.Create
No prefix Default OCL evaluation. House.allinstances

Separate expressions with an empty line. When you select Execute, the debugger executes the expression at the current row, not every expression in the editor.

Press Ctrl+Enter in the editor to open the code-completion list and select an available operator or member.

Create a House and inspect its initial state

Create an object with action language:

action: House.Create

Execute the expression, then run House.allinstances again. The result now contains a House. Its state is Plan, as specified by the initial transition in the state machine.

Store a selected object in M1

The debugger provides M1, M2, and M3 as temporary memory variables, similar to calculator memory registers.

  1. Select a House row in the result list.
  2. Choose Selected to M1.
  3. Use M1 in a later expression.

M1 is a collection, even when it contains one object. Select the first object before calling the trigger:

action: M1.first.StartConstruction

If the guard is false, the House does not transition to Construction. Inspect the address condition with expressions such as:

M1.first.Address.isnull

M1.first.Address.notnull

This check exposes an important data-quality case: an empty address string is not null. Use a null-or-empty check in the guard when a blank address must also prevent construction.

Test a ViewModel UI

A ViewModel folds a part of the model into a definition for presenting and collecting data. The ViewModel editor is not the final executing UI; it defines what data is available and how the UI can be arranged. MDriven can construct a UI from the ViewModel definition and its placing hints.

To inspect the data through the ViewModel in the debugger:

  1. Re-read the model if you made changes, and save it when prompted.
  2. Select the House objects in the debugger result.
  3. Choose Result as root ViewModel UI.
  4. Inspect the values exposed by the ViewModel and edit values where the UI permits it.
  5. Save the changed data when the Save button becomes available.

For example, a House address can point to TheStreet. Editing an address value does not create that related Street object if TheStreet has not been set. Add an action to the ViewModel for that purpose.

  1. Return to the ViewModel editor.
  2. Turn on the menu.
  3. Add a column prepared for an action.
  4. Give the action a name such as CreateStreet.
  5. In the action-language expression, assign a newly created Street to the House:
self.TheStreet := Street.Create
  1. Re-read the saved model in the debugger.
  2. Open Result as root ViewModel UI again and run CreateStreet.
  3. Enter the address data and save.

Each use of this action creates a Street object. You can verify the created objects with:

Street.allinstances

See also

Understanding ViewModels

Understanding ViewModels

This section helps you decide what a ViewModel is and how to use one when you design a view of your model in MDriven.

A ViewModel is a transformation layer that selects and organizes information from the model for one specific purpose. That purpose is often a user interface, but it can also be an API, a JSON or XML extraction, or a report.

The model contains the complete business structure: classes, attributes, and associations. A ViewModel gives that structure a perspective that fits one use case. For example, a HouseView can show a house's street number and the name of its street, without requiring every use of House to present the same information.

What a ViewModel contains

A ViewModel is made up of ViewModel classes and columns. A ViewModel class defines information that the view presents. Its columns can expose attributes from the model, navigate associations, or use an expression.

In a HouseView rooted in House:

ViewModel item Example What it shows
Root context House The house currently being viewed.
Column StreetNumber The street number of that house.
Column through an association TheStreet.Name The name of the street associated with that house.
Derived display value self.TheStreet.Name + ' ' + self.StreetNumber.asstring A text representation such as a street name followed by the street number.

In this context, self means the root model object: the current House. OCL (Object Constraint Language) expressions use that context to read model information and define what the ViewModel shows.

Rooted and unrooted ViewModels

Choose the ViewModel type based on where its data starts.

Type Starting point Typical use Example
Rooted ViewModel A specific model object supplied as the root. The root ViewModel class has self as that object. Show one object and related information. A HouseView shows one house and information reached through its TheStreet association.
Unrooted ViewModel No specific model object is supplied as the root. The root ViewModel class has no self context. Search for or show a list of objects. A seeker finds instances of a class and shows them in a list.

Use a rooted ViewModel when the user starts with one known object. Use an unrooted ViewModel when the user needs to find objects or work with a complete or partial list. For an unrooted ViewModel, use a collection such as ValueStore.allinstances to obtain model objects; the ViewModel class that represents each item then has that item's context.

Put logic in the right place

A ViewModel can contain logic that is unique to the view's context, such as transforming model information into the fields needed by a particular screen. This keeps presentation-specific transformations out of the user interface and makes the ViewModel reusable by different user interfaces, APIs, or reports.

Put logic in the model when it is not unique to one view and should be reused. The ViewModel can then call or present that model behavior. Keeping reusable rules in the model avoids creating separate copies of the same rule for multiple views.

For the HouseView example, constructing a display value from TheStreet.Name and StreetNumber is view-specific transformation. The ViewModel defines the perspective; the model remains the source of the underlying House and Street information.

Why use a ViewModel instead of placing logic in the UI?

A ViewModel separates the user interface from the model and its business logic. You can test ViewModel logic without testing the UI, reuse the same ViewModel for different user interfaces, and design the information available to a view without exposing the complete business object.

The ViewModel also provides the data and behavior used by the view. ViewModel actions can handle user interactions, data operations, validation, and state changes while coordinating with the model.

See also