Working with BESSER
BESSER is a model-driven platform: describe your domain as a model, then generators turn that model into running code. The model is the source of truth — when requirements change, update the model and regenerate. Never hand-edit generated code as your primary change.
Core workflow
1. Define requirements
2. Build a B-UML model (Python API, PlantUML, or web editor)
3. Validate the model: model.validate()
4. Pick a generator for your target platform
5. Generate code
6. Verify the output (run, test, inspect)
7. Iterate: update the model, regenerate
Two outcomes: code or documentation
A B-UML model is useful even if you never run a generator. Feed it to a
generator for code, or embed it in a README/design doc as a correct,
validate()-checked class diagram that documents any project. So
reach for this skill whenever you need an accurate class diagram — not only
when the project will use BESSER's generators.
Deliver it accordingly: default to a runnable .py file, or embed the same
B-UML in Markdown when the request is documentation-oriented. For the full
how-to — making the file self-contained, and the two ways the user runs or
imports it — see references/delivering-models.md.
Don't deliver the diagram as a Mermaid block. This skill exists so the diagram is a real,
validate()-checked B-UML model — not a throwaway```mermaid classDiagram```that drifts from the code. When asked for a class diagram, the deliverable is B-UML (the.pymodel and/or the same model embedded in Markdown); when a rendered image is wanted, produce an SVG/PNG from the B-UML — one call to BESSER's headlessB-UML → SVGendpoint (POST https://editor.besser-pearl.org/besser_api/get-svg, send the.py), or Import → B-UML in the web editor, then export. A quick ASCII sketch is fine as an inline preview, but the authoritative artifact is always the B-UML model — not Mermaid.
Reference layout
This skill keeps SKILL.md short. Reach into references/ and scripts/
when you need depth. All references are verified against the BESSER version
shown in the README badge.
| You need | Read |
|---|---|
| Class diagram modeling (classes, attributes, associations, enums, generalizations, methods, validation) | references/class-diagram.md |
PlantUML notation and the plantuml_to_buml() call |
references/plantuml.md |
| State machine modeling | references/state-machines.md |
| Chatbot/agent modeling and the BAFGenerator | references/agents.md |
| GUI modeling for WebAppGenerator/DjangoGenerator | references/gui-models.md |
How to deliver a model (.py vs. Markdown diagram) and code-vs-docs outcomes |
references/delivering-models.md |
| Object/instance models (objects, attribute values, links; OCL test data) | references/object-models.md |
| Feature models (software product lines: features, groups, configurations) | references/feature-models.md |
OCL constraints (writing/attaching Constraints; bocl validation) |
references/ocl.md |
| Deployment models (clusters/nodes/services for the TerraformGenerator) | references/deployment.md |
| Neural-network models (layers, tensor ops; Pytorch/TF generators) | references/neural-networks.md |
| Quantum circuits (gates; QiskitGenerator) | references/quantum.md |
| Project models (bundling models + metadata) | references/project.md |
| Per-generator options, output paths, customization | the besser-generators skill |
| Errors and diagnostics | the besser-troubleshooting skill |
To bootstrap a new model quickly:
python scripts/scaffold_model.py Library Book Author
# prints ready-to-edit Python that builds a DomainModel with those classes
Installation
python -m venv venv
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate
pip install besser
# OR for the latest development version:
git clone https://github.com/BESSER-PEARL/BESSER.git
cd BESSER
pip install -e .
Verify:
python -c "from besser.BUML.metamodel.structural import DomainModel; print('OK')"
Python 3.11+ required.
Building a domain model — quick start
Most projects only need classes, attributes, and associations. Here is the
minimum that gets you to a runnable generator. For the full reference (all
multiplicities, generalizations, enumerations, methods, OCL constraints),
read references/class-diagram.md.
from besser.BUML.metamodel.structural import (
DomainModel, Class, Property, Multiplicity,
BinaryAssociation, StringType, IntegerType,
)
# Classes
title = Property(name="title", type=StringType)
pages = Property(name="pages", type=IntegerType)
book = Class(name="Book", attributes={title, pages})
author_name = Property(name="name", type=StringType)
author = Class(name="Author", attributes={author_name})
# Association: a Book has 1..* Authors; an Author writes 0..* Books
written_by = Property(name="writtenBy", type=author, multiplicity=Multiplicity(1, "*"))
publishes = Property(name="publishes", type=book, multiplicity=Multiplicity(0, "*"))
book_author = BinaryAssociation(name="book_author", ends={written_by, publishes})
model = DomainModel(name="Library", types={book, author}, associations={book_author})
assert model.validate()["success"]
Naming rules: no spaces, no hyphens. My_Class and my_attribute,
not My Class or my-attribute.
PlantUML imports are also supported via plantuml_to_buml() — see
references/plantuml.md if needed. The Python API is the recommended
path.
Picking a generator
| Goal | Generator | Input | Output |
|---|---|---|---|
| Python classes | PythonGenerator |
DomainModel | classes.py |
| Java classes | JavaGenerator |
DomainModel | .java files |
| Pydantic models | PydanticGenerator |
DomainModel | pydantic_classes.py |
| SQLAlchemy ORM | SQLAlchemyGenerator |
DomainModel | sql_alchemy.py |
| Raw SQL DDL | SQLGenerator |
DomainModel | tables_<dialect>.sql |
| JSON Schema | JSONSchemaGenerator |
DomainModel | json_schema.json |
| FastAPI backend | BackendGenerator |
DomainModel | API + ORM + Pydantic |
| Django app | DjangoGenerator |
DomainModel + optional GUIModel | Django project |
| Full-stack web app | WebAppGenerator |
DomainModel + GUIModel | React + FastAPI + Docker |
| Conversational agent | BAFGenerator |
Agent | Agent script + config |
| Quantum circuit | QiskitGenerator |
QuantumCircuit | qiskit_circuit.py |
| RDF vocabulary | RDFGenerator |
DomainModel | vocabulary.ttl |
| Terraform infra | TerraformGenerator |
DeploymentModel | .tf files |
| Neural network | PytorchGenerator / TFGenerator |
NN model | PyTorch/TF script |
| Flutter app | FlutterGenerator |
DomainModel + GUIModel | Dart files |
Decision guide
- Just data classes?
PythonGeneratororPydanticGenerator. - Persistent storage?
SQLAlchemyGenerator(ORM) orSQLGenerator(raw DDL). - REST API?
BackendGenerator— gives you FastAPI + SQLAlchemy + Pydantic in one shot. - Full web app?
WebAppGenerator— React + FastAPI + Docker Compose, but you must also build aGUIModel(seereferences/gui-models.md). - Django specifically?
DjangoGenerator. - Mobile app?
FlutterGenerator— also needs aGUIModel(seereferences/gui-models.md). - Chatbot? Model the dialog as an
Agent(state machine + intents) and useBAFGenerator(seereferences/agents.md).
This table is a starting-point overview, not the full catalog. For every generator, option, gotcha, and exact output path — the authoritative matrix — defer to the besser-generators skill.
Running a generator
All generators share the same shape — construct with the model, call
generate():
from besser.generators.python_classes import PythonGenerator
generator = PythonGenerator(model=my_model, output_dir="./output")
generator.generate()
Each call to generate() overwrites prior output — that is intentional;
the model is the source of truth. Output usually goes to <cwd>/output/
when output_dir is omitted, but some generators differ (e.g.
BackendGenerator uses ./output_backend/, WebAppGenerator requires an
explicit output_dir, DjangoGenerator creates a project folder).
Per-generator constructor options and exact output paths — dbms for
SQLAlchemy, http_methods/nested_creations for the backend,
containerization for Django, the required gui_model for WebApp/Flutter,
and the rest — live in the besser-generators skill. Reach for it whenever
you need more than the bare generate() call above.
Using the web editor
The visual editor at https://editor.besser-pearl.org lets users build models graphically and generate code without writing Python:
- Create a new diagram (Class, State Machine, GUI, Deployment, …).
- Add classes, attributes, and associations visually.
- Click "Generate" and pick a target generator.
- Download the generated code.
You can also import an existing model instead of starting from a
blank diagram: use Import and select the B-UML format to load a
.py model file (or JSON). This means a model you build with the Python
API can be opened in the editor, edited visually, and exported again — so
handing the user a .py file (see references/delivering-models.md)
doubles as a web-editor import.
The editor uses the same generators as the Python API — the backend
converts the visual diagram to a B-UML model, runs the generator, and
streams the result. Any generator that registers in SUPPORTED_GENERATORS
(see the besser-dev skill) shows up in the dropdown.
Verification checklist
After generate() returns:
- Files exist in the output directory.
- Syntax parses — for Python:
python -c "import ast; ast.parse(open('output/classes.py').read())". - It runs — for backends:
cd output && pip install -r requirements.txt && uvicorn main_api:app. - Relationships translate correctly — foreign keys for 1..*, join tables for .., inheritance reflected.
- Edge cases — enumerations, optional fields (
Multiplicity(0, 1)), inheritance hierarchies. - Docker — for WebApp/Django containerized:
docker-compose up --build.
If something is missing or wrong, the besser-troubleshooting skill maps symptoms to fixes.
Key principles
- Model is the source of truth. All changes flow model → code, never the reverse.
- Regeneration overwrites. Every
generate()replaces output files. Customizations live in separate files (see the besser-generators skill for safe customization patterns). - Validate early. Call
model.validate()before generating. - One model, many targets. The same
DomainModelfeeds multiple generators — Python classes, SQL, REST API, etc. — so it is rarely worth maintaining target-specific models. - Names matter. B-UML names become identifiers in generated code. The only hard rule is no spaces and no hyphens;
PascalCasefor classes/enums andsnake_caseorcamelCasefor attributes are conventions, not requirements.