LabsModeling foundations

Lab 03Add behavior to your model

The research model gains an OCL constraint, method bodies in BAL and Python and a state machine, and you watch them run in a generated FastAPI backend.

Time
50 min
Level
Intermediate
Runs with
Browser, Python
Do first
Lab 1

You'll learn to

  • Write an OCL invariant and check it with Quality Check
  • Test a constraint against concrete objects in an object diagram
  • Implement methods in the BESSER Action Language (BAL) and in Python
  • Link a state machine to a method as its implementation
  • Generate a FastAPI backend and call the modeled methods from Swagger UI

You'll need

  • A modern browser
  • Python 3.11 or 3.12 to run the generated backend (the modeling part needs only the browser)
  • The project from the first lab, or the starter file below

Files for this lab

A class diagram describes structure: which data exists and how it is connected. Real systems also have rules and behavior. A paper has a page limit, an accepted paper must be marked as accepted, a review goes through stages. In this lab you add all three kinds to the research model from Draw your first class diagram: an OCL constraint, methods with real bodies, and a state machine.

Then you generate a backend from the model and run it. The constraint becomes input validation, and each method becomes an HTTP endpoint you can call.

Open the research project and add a page count

  1. In the editor, open your Research_Lab project from the first lab. If you do not have it, download the starter project and open File > Import > Project file (.json / .py) to import it.
  2. Double-click Paper and add the attribute pages: int in the + attribute: str field.

Write an OCL constraint

OCL (Object Constraint Language) states rules that every valid instance must satisfy. An invariant is a rule about one class: context <Class> inv <name>: <boolean expression>, where self is the instance being checked.

  1. Drag OCL Constraint from the palette onto the canvas, next to Paper, and double-click it.
  2. In the OCL Constraint field, type:
context Paper inv page_limit: self.pages <= 12
  1. In Description (shown to end-users when validation fails) Optional, type A paper can have at most 12 pages. This text is the message people see when the rule is broken.
The OCL Constraint panel with the invariant page_limit and its description, next to the Paper class
The note on the canvas shows the stereotype «inv» and the start of the expression.
  1. Click Quality Check.
The notification Valid Constraints, listing Paper inv page_limit with its description, followed by Diagram is valid
Quality Check parses the constraint against the classes and attributes of the diagram.

Test the constraint on an object diagram

Quality Check on a class diagram only checks that the constraint is well formed. To see it judge data, you build concrete objects in an object diagram.

  1. Click Object in the left sidebar. The palette lists one object per class of your class diagram, such as paper_1 : Paper and researcher_1 : Researcher. The References bar at the top shows which class diagram the objects come from.
  2. Drag paper_1 : Paper onto the canvas and double-click it. Set title to Low-code for research software, pick a date for submitted, type false for acceptance and 15 for pages.
The paper_1 object with pages = 15 and its Object panel with one field per attribute
Each attribute of the class gets a value field of the right kind: text, a date picker, true or false.
  1. Drag researcher_1 : Researcher below it and give it a name and an institution. Drag conference_1 : Conference next to it and fill in its name, dates and acronym.
  2. A paper needs an author and an event, as the multiplicities say. Select paper_1 and drag from one of its connection points to researcher_1. Double-click the new link, open the dropdown that shows No Association and choose authorship.
The object link between paper_1 and researcher_1 with its panel set to the authorship association
A link is an instance of an association. Choosing the association also fills in the link name.
  1. Link paper_1 to conference_1 the same way and choose presented_at.
The object diagram with paper_1 linked to researcher_1 and to conference_1
Three objects and two links. Conference objects also show the attributes inherited from ResearchEvent.
  1. Click Quality Check. The constraint is now evaluated against paper_1:
The notification Invalid Constraints: Paper inv page_limit, Constraint violation: A paper can have at most 12 pages.
The description you wrote is the explanation.
  1. Double-click paper_1, change pages to 10 and run Quality Check again.
The notification Valid Constraints: Paper inv page_limit evaluates to True, followed by Diagram is valid
The same rule, now satisfied.

Implement methods in BAL

The BESSER Action Language (BAL) is a small, statically typed language for method bodies. Its syntax resembles Java and Python: def name(param: type) -> return_type { ... }, this for the current object, if (...) { ... } else { ... }, statements ending in ;. The BAL overview lists everything else.

  1. Click Class in the sidebar and double-click Paper.
  2. Under Methods, type + length_class(): str in the + method(param: str): str or → field and press Enter.
  3. Below the new method, open the Type: dropdown, which shows None (UML), and choose BESSER Action Language. A code editor opens with a template that starts with def length_class() -> nothing {.
  4. Replace the whole template with:
def length_class() -> str {
    if (this.pages > 8) {
        return "long";
    } else {
        return "short";
    }
}
The Paper panel with length_class implemented in BAL: the type dropdown shows BESSER Action Language and the editor holds the method body
The signature field is now read from the code and greyed out.
  1. Add a second method with a parameter. Type + reading_time(pages_per_hour: int): float, choose BESSER Action Language, and replace the template with:
def reading_time(pages_per_hour: int) -> float {
    return this.pages * 1.0 / pages_per_hour;
}

BAL checks types. this.pages / pages_per_hour divides two int values and is an int, which does not match the declared float; the generator then stops with A return statement in function reading_time is returning an invalid type, returns int rather than float. Multiplying by 1.0 first makes the expression a float.

Add a Python method that changes the paper

A method can also be written in Python. You use it here for a method that changes the object.

  1. In the Paper panel, add + accept(): bool.
  2. Set its Type: to Python Code and replace the template with:
def accept(self):
    """Mark the paper as accepted."""
    self.acceptance = True
    return self.acceptance
The accept method with Type Python Code and its Python implementation
Python methods take self and change the object directly.
The class diagram with pages added to Paper, its three methods, and the page_limit OCL note
The model so far: one invariant and three implemented methods.

A method can also be implemented by a state machine. The Data Modeler perspective hides state machines, so turn them on first.

  1. Click Settings in the sidebar. Under Modeling Perspectives, tick State Machine Diagram. A State entry appears in the sidebar.
The Modeling Perspectives list in Settings with State Machine Diagram ticked
Turning a perspective off hides it from the sidebar but keeps its models.
  1. Click State. From the palette, drag an initial node (the filled circle), two State elements and a final node (the circle with a ring) onto the canvas. Double-click the states and name them Submitted and Reviewed.
  2. Connect them from left to right: select an element and drag from one of its connection points to the next one.
A state machine: initial node, Submitted, Reviewed and final node, connected by transitions
A minimal review process. Transitions can carry events and conditions; you can leave them empty here.
  1. Go back to Class, double-click Paper and add + review(). Set its Type: to State Machine. A second dropdown appears; choose State Machine Diagram, the name of the state machine’s tab.
The review method with Type State Machine and the State Machine Diagram selected
The method now refers to the state machine diagram.

Generate the backend and call the methods

  1. With the class diagram active, open Generate > Web > Full Backend. The editor downloads backend_output.zip.
The Generate menu with Web expanded: Django Project, Spring Project, Full Backend and Web Application
Full Backend: a FastAPI application with SQLAlchemy and Pydantic.
  1. Unzip it into a new folder. It contains main_api.py, routers/, pydantic_classes.py, sql_alchemy.py, database.py, bal_stdlib.py and requirements.txt. Open a terminal in that folder, create and activate a virtual environment as in Model in Python with B-UML, then install and start the server:
python -m pip install -r requirements.txt
uvicorn main_api:app --reload
  1. Open http://127.0.0.1:8000/docs. Swagger UI lists every endpoint. The methods are under Paper Methods:
Swagger UI section Paper Methods with POST endpoints for accept, length_class and reading_time
One POST endpoint per modeled method: /paper/{paper_id}/methods/<name>/.
  1. Create the data. In Swagger UI, open an endpoint, click Try it out, paste the body and click Execute. Create a conference with POST /conference/, then a researcher with POST /researcher/:
{"name": "MODELS 2026", "start": "2026-10-04", "end": "2026-10-09", "mode": "in_person", "acronym": "MODELS"}
{"name": "Alice", "institution": "LIST"}
  1. Try to create a paper that breaks the page limit with POST /paper/:
{"title": "Too long", "submitted": "2026-04-01", "acceptance": false, "pages": 15, "authors": [1], "event": 1}

The server answers with code 422. Your OCL invariant became a validator in pydantic_classes.py:

{"detail":[{"type":"value_error","loc":["body","pages"],"msg":"Value error, pages must be <= 12","input":15,"ctx":{"error":{}},"url":"https://errors.pydantic.dev/2.6/v/value_error"}]}
  1. Send the same body with "title": "Low-code for research software" and "pages": 10. This time you get code 200 and a paper with "id": 1.
  2. Call the methods on paper 1. For POST /paper/{paper_id}/methods/length_class/, enter 1 as paper_id and execute:
The Swagger response of length_class for paper 1: status executed and result long
The BAL body ran on the stored paper, which has 10 pages.

For reading_time, send the parameter inside params:

{"params": {"pages_per_hour": 4}}
{"paper_id":1,"method":"reading_time","status":"executed","result":"2.5","output":null}

Then call accept with an empty body ({}) and read the paper back with GET /paper/{paper_id}/:

{"paper":{"id":1,"acceptance":true,"event_id":1,"pages":10,"title":"Low-code for research software","submitted":"2026-04-01"},"researcher_ids":[1]}
  1. Finally, call POST /paper/{paper_id}/methods/review/. It answers with code 501 and Method 'review' of Paper is modeled but has no implementation - 501, never a fake success: the endpoint exists because the method is in the model, but its state machine is not translated into code.

The data is stored in SQLite, in data/Class_Diagram.db inside the backend folder; delete that file to start with an empty database. Stop the server with Ctrl+C.

Exercise: more rules and more behavior

Show a solution

The expression compares two attributes of the same object: context ResearchEvent inv dates_in_order: self.end >= self.start. The generated backend then rejects such an event with code 422 and Constraint 'dates_in_order' violated: self.end >= self.start. Because Conference inherits from ResearchEvent, the rule applies to conferences and workshops too.

Show a solution

The body is a single return of a comparison between this.pages and the parameter. Send the parameter as {"params": {"limit": 8}}. Remember that BAL statements end with ;.

Further reading: OCL in B-UML, the BESSER Action Language, the object diagram guide and the backend generator.