Structura Documentation
What's is Structura?
Structura allows to edit Class Models in a graphical way.
Etymology: from struō (“to build”) + -tūra (“concrete action noun suffix”).
It is created using the Daga library as a graphical editor foundation and Essential as core metamodel.
The code generation allows a great level of precission, customization, and maintainability on defining custom-made targets based on ad-hoc code-gen.
Feel free to try for free all our code generators sampled here.
If you need a custom code generator to target your specific stack feel free to reach us for a quote. We are experts in tailor-made code generation ensuring compliance by-design.
Graphical Notation
Graphical notations resembles a classical UML class model.
Class
No surprises here. A class is represented as a rectangle (like the classical UML representation).

Enumeration
Enumerations are represented as a rectangle with a different color.

Associations
Sample composition between Invoice and InvoiceLine.

Inheritance relations
Sample inheritance relationship.

Textual Syntax
Structura defines a concise syntax to help reviewing models in textual form. This format is useful for:
- humans: to create or review
- tools: storing it on git and tracking changes, or
- AIs: to read or to produce.
The following concepts are used in the textual syntax:
Comments
You can use regular comments (C style), inline or multiple lines:
// Inline comment
/* Multiline comment
2nd line.
*/
Namespaces
Namespaces creates logical buckets or folders to contain concepts. They declares a naming scope and thus, it must be unique.
namespace Earth.Europe.Spain
{
// Definitions ...
}
Classes
A class defines a type for objects. All objects created/instanciated from the same class have the same properties, methods and behaivour.
class Customer
{
// Properties ...
}
Properties
Properties are defined inside the classes with a type and can be declared optional (denoted with '?' as a postfix of the type) or mandatory in other case. Note the optional mark is indicated in the type and not in the property. Types are specified before the property name.
class Customer
{
string Name;
string Surname;
int? Age;
}
Enumeration
Enumerations defines a set of fixed values with a code and a name. These are used as semantic types to better describe your domain.
enum Color
{
Undefined = 0
Red = 1,
Yellow = 2,
Green = 3
}
Codes can be strings also. Example.
enum Day
{
Monday = "mon"
Tuesday = "tue",
Wednesday = "wed",
// ...
}
Associations
Associations establish relationships betweens classes. When relating two classes (called here source & target) you must describe the following aspects:
- Composition: use the
compositionkeyworkd to indicate it. - On the source:
- Class name: the source class
- Source Role: the role to name this end fo the relation.
- Source Cardinality: minimum and maximun cardinality with the standard semantics used in UML.
- Class name: the target class
- Target Role: the role to name this end fo the relation.
- Target Cardinality: minimum and maximun cardinality with the standard semantics used in UML.
Allowed cardinalities and samples:
| Cardinality | Meaning |
|---|---|
1 | Exactly one |
1..1 | Exactly one (equivalent to 1) |
0..1 | Zero or one (optional) |
* | Zero or more (unbounded) |
0..* | Zero or more (explicit form, equivalent to *) |
1..* | One or more (at least one required) |
Note: associations where both sides have a minimum cardinality > 0 (e.g. 1..1 <>--- 1..1) are not allowed. The problem is that both ends require at least one instance of the other, creating a circular dependency that cannot be resolved at creation time. At least one side must be optional (0..1 or 0..*).
// 1 — exactly one customer per order
association Order (order) 1 <>-- Customer (customer) *;
// 1 — one invoice owns its lines (composition)
association composition Invoice (invoice) 1 <>-- InvoiceLine (lines) *;
// 0..1 — an order may optionally have one applied coupon
association Order (order) * <>-- Coupon (coupon) 0..1;
// * — a product can appear in zero or more order lines
association OrderLine (lines) * <>-- Product (product) 1;
// 0..* — explicit form; a tag can label zero or more articles
association Tag (tags) 0..* <>-- Article (articles) 1;
// 0..* — an order can have zero or more lines
association Order (order) 1 <>-- OrderLine (lines) 0..*;
Inheritance relations
Inheritance relationship can be expresed with this syntax:
class Child : Parent {}
Type System
Properties must have types. Primitives types supported by Structura are:
| Keyword | Description |
|---|---|
string | Common string datatype. It should be size constrained. |
char | A single char. |
bool | A boolean type (true or false). |
date | A date only value (yyyy-mm-dd). |
time | A time only value (hh:mm:ss). |
datetime | A complete timestamp. |
int | Integer. |
long | Long integer. |
decimal | Decimal type. |
float | Float type. |
Decorators
Classes and properties can be decorated (Technically in UML this is called a stereotype).
Decorators enrich the model with specific semantics.
You can create freely your own decorators, but there also a list of predefined decorators with specific pourposes.
Class decorators
| Decorator | Sample | Semantics |
|---|---|---|
doc | [doc("A class description")] | Provides a detailed description for the class. |
color | [color("red")] | Provides a color for a class or enum. This allows you to arbitrarily classify a model or to follow conventions like UML Color |
table | [table("my_table")] | Provides a table name for a class. This allows you to specify the database table to which a class maps. It is required to distinguish the database table when there are two or more classes with the same name. |
displayName | [displayName("orderNumber")] | Names the property whose value represents an instance of the class when it is shown as a reference inside another entity (for example, a to-one column in a generated Angular CRUD table). Without it, the referenced object renders as [object Object]. |
Property decorators
| Decorator | Sample | Semantics |
|---|---|---|
doc | [doc("A property description")] | Provides a detailed description for the class. |
key | [key] | Select the key field for the class. This will be used as a primary key and object identifier on generated code. |
index | [index] | Created an index for this field to increase the performance on query operations. |
maxsize | [maxsize(20)] | Provides a maximum size in characters for a string. In general, it is importan to limit the size of string fields due to performance issues when creating the databa schema. |
minsize | [minsize(3)] | Provides a minimum size in characters for a string. Validation pourposes. |
mail | [mail] | Email domain type. Provided specialized editors and validation. |
money | [money] | Money domain type. Provided specialized editors and validation. |
gt | [gt(0)] | Validation. Greather than x. |
gte | [gte(0)] | Validation. Greather or equals than x. |
lt | [lt(42)] | Validation. Lower than x. |
lte | [lte(42)] | Validation. Lower or equals than x. |
regex | [regex("^\w+\d*$")] | Validation. String value should match the regular expression. |
Colors
Structura provides a palette of colors to help you to classify and organize the concepts in your model on your own with your custom criteria.
pink yellow blue green red orange purple gray However, by default, Zeus uses UML Color criteria and will pre-clasify the concepts following this criteria:
pinkMoment-interval — Represents a moment or interval of time for legal or business reasons.yellowRole — Is it a way of participating in an activity (by either a person, place, or thing).blueDescription — Is it simply a catalog-entry-like description which classifies or 'labels' an object.greenParty, place, or thing — Something tangible, uniquely identifiable.
If you want other classification criteria ask Zeus about it.
Validation
Model can be validaded before code generation.
A first set of rules applies to every model regardless of the selected code generator (see General Validations). On top of that, each target generator runs its own additional rules, tailored to the constraints of the language or platform it produces code for.
General Validations
These checks run for every model, independently of the target code generator:
- Naming — model, namespace, class, enumeration, enumeration item, property, method and decorator names must be valid identifiers (start with a letter or underscore, followed by letters, digits or underscores). Namespaces additionally allow dot-separated segments (e.g.
Earth.Europe.Spain). - Key property — every class should have a key property, either decorated with
key, inferred by convention (see Conventions), or inherited from an ancestor class. Generators that require a key (like Java and .NET 10 base) raise this as an error; otherwise it is a warning. - Duplicate properties — a class cannot declare the same property name twice.
tableattribute — a class can have at most onetableattribute, with exactly one (non-empty) parameter. The same table name cannot be reused by two different classes, and it cannot collide with the plain name of another class that has notableattribute of its own.- Types — properties must reference one of the primitive types (see Type System) or an enumeration defined in the model — the same options offered by the type dropdown in the property editor. If two enumerations in different namespaces share the same simple name, referencing that simple name is ambiguous and must be qualified. String properties without a
maxsizeraise a warning, since unbounded strings are discouraged. - Decorator parameters — each decorator enforces its own parameter shape:
doc,regexanddisplayNamerequire exactly one string parameter;maxsize,minsize,gt,gte,ltandlterequire exactly one integer parameter;key,index,mailandmoneyaccept no parameters. Decorators are also checked for type-compatibility: numeric decorators (gt/gte/lt/lte) cannot be applied tostringproperties, and string decorators (maxsize/minsize/mail/regex) cannot be applied to numeric,bool,date,time,datetimeorcharproperties (moneyis allowed on both numeric and string properties). - Associations — cardinalities must be one of the valid forms (
1,0..1,*/0..*,1..*); source and target classes must exist in the model; both ends cannot be mandatory at the same time (minimum cardinality > 0 on both sides is rejected — see Textual Syntax); a role left blank falls back to the referenced class's name, same as omitting it entirely; and two associations cannot declare the same effective role on the same class end. - Composition — a class can be the target of at most one composition relationship. Additionally, the source-side cardinality of a composition (
<>--) must be1or0..1— a source cardinality of*or1..*is rejected, since a composed part cannot belong to more than one parent simultaneously. - Enumerations — two checks apply to enumeration members:
- Ordinal collisions — when items mix explicit numeric codes with implicit ones (auto-incremented from the last explicit value), the resulting ordinal values must all be distinct. If an implicit ordinal collides with an explicit one declared later, an error is reported naming both members.
- Mixed string codes — if at least one item carries a string code, any item missing a code will have one inferred. A warning is raised listing the members without a code, since inferred values may not match the intended semantics.
- Property / association role clashes — a declared property cannot have the same name as its own class, nor the same name as a navigation property generated by an association on that class.
- Inheritance references — the parent class named in an inheritance relation must exist in the model, and circular inheritance chains (a class that, directly or transitively, inherits from itself) are detected and reported with the full cycle path.
Inheritance Property Conflicts
When a class inherits from another, redefining a property with the same name as one already declared in an ancestor class is checked differently depending on the target generator:
- .NET 10 — if the repeated property has the same name as the ancestor's inferred primary key (see Conventions), code generation is blocked with an error. If it matches any other (non-key) inherited property instead, a warning is raised: the property is still generated, but it hides the inherited one in the generated C# code. When targeting SQL Server, repeating an inherited property name with a different type is always an error, since the underlying database columns must match.
- Java — redefining a property with the same name as any property inherited from an ancestor — whether it's the key or not, and regardless of type — is always an error; the property must be renamed.
No Multiple Inheritance
The graphical and textual editors don't block declaring more than one parent class for the same class, but the Java and .NET 10 base generators only support single inheritance. If a class ends up with more than one parent, validation reports an error listing every declared parent class.
Java Generator
When validating for the Java target, on top of the general rules above:
- A key property is required on every class.
- Reserved keywords — three kinds of names are checked against the Java reserved keyword list (JLS 17 §3.9, plus the literals
true/false/null):- A property whose name, once converted to the camelCase used for Java fields, matches a keyword (e.g.
class,default,package,synchronized,var…) is rejected. - An enum item whose key exactly matches a keyword is rejected — enum constants are emitted verbatim with no case conversion.
- An association role — whether set explicitly or inferred from the class name — that produces a keyword after camelCase conversion is rejected, since it becomes the field name of the generated navigation property.
- A property whose name, once converted to the camelCase used for Java fields, matches a keyword (e.g.
- Any property name that collides with an inherited property is always an error for Java — whether or not the colliding property is the ancestor's key (see Inheritance Property Conflicts above).
.NET 10 Base Generator
When validating for the .NET 10 base target, on top of the general rules above:
- A key property is required on every class.
- A property — or an association role that generates a property — cannot be named the same as its own owning class.
- Namespace collisions — a class or enumeration cannot share its name with one of its own namespace's segments, nor with any namespace segment used elsewhere in the model, since this produces a C# naming conflict in the generated code.
- Reserved layer names — a class or enumeration cannot be named after one of the fixed top-level layers the generator scaffolds:
Contracts,Controllers,Infrastructure,Models,Operation,Program,Repositories,Services. - Duplicate class names — with namespaces kept, two classes sharing the same simple name require a
tableattribute to tell them apart; with namespaces flattened, classes sharing the same simple name across different namespaces are rejected outright, since they would collide once namespaces are removed. - Database-specific table names — when a target database is selected, the effective table name (the
tableattribute value, or the class name otherwise) is checked against that engine's identifier rules: SQL Server (max 128 characters, unquoted identifier), MySQL (max 64 characters, unquoted identifier), MongoDB (no$, null byte, space, or leadingsystem.prefix). For SQL Server specifically, an inherited property repeated with a different type is always an error, since the underlying database columns must match (see Inheritance Property Conflicts above). - Property / FK role clashes (SQL Server only) — when the target database is SQL Server, a declared property cannot have the same name as the FK property an association generates on that class (e.g. a scalar-cardinality association contributing a
CustomerId-style foreign key). This is checked separately from the general navigation-property clash above, since FK properties only exist for relational generation.
Some interested usages here is to apply profiles for business compliance validation like GDPR, PCI/DSS, and other security and quality standars to be enforced.
To be further defined.
Angular Generator
When validating for the Angular target, on top of the general rules above:
displayNameon referenced classes — when a class sits at the single-valued (to-one) end of a non-composition association, the other entity gets a navigation column typed by it in the generated CRUD table. If that referenced class has nodisplayNamedecorator, a warning is raised, since the object would render as[object Object]. Add[displayName("<property>")]to the class to mark which property represents the instance. Collection (to-many) ends render as an item count, and composition children are excluded, so neither triggers this warning.
Conventions
When generating the code, a function will be executed to attempt to infer the primary key for each class in the model.
According to the rules followed by this process, a property will be considered the primary key if it meets the following criteria, in order of priority:
- The property is already decorated with
key. - The property is named
id. - The property is named as the class name followed by
id. - The property is named
idfollowed by the class name. - The property is named
name. - The property is of type
DateTimeand is namedTimestamp.
Code Generation
Structura can be used to implement code artifacts for different targets:
- A backend with .Net Core 10 with persistence in SqlServer, MongoDb or MySQL, dockerized + Redis Cache. Rest API publised as OpenApi 3.1. Architecture: simple: contracts (DTOs), controller, (entitites) service, & repository.
- Angular code including proxy services to connect with the backend.
- A Vibe Coding generator (experimental).
- Model documentation.
- Others to come... (on demand): Compliance, embeded systems, real-time code, custom tech-stack, etc.
Frequently Asked Questions
Some questions we need to clarify:
Why another UML editor?
In the Artificial Intelligence era:
- we think there is space to innovate providing a Web editor (low-code) to design at the right level of abstraction.
- we think the value in the model (changes slowly, more stable), and less value is in the code (can change faster).
- AI & LLMs can help you to create the model. You can curate the result and prevent AI hallucinations.
- Code generation can provide you a solid start (80% of what you need accordingly to Pareto).
- AI & LLMs and tradictional coding can help you again to complete faster the missing 20%.
Who owns the model I created?
You. We do not use the models for anything else than proving the service.
Who owns the code I generated?
You. Please, recognize in the credits the source tool (https://structura.tools) but feel free to use the code in your projects with no limits for personal or bussiness usages, including commercial distribution.
- Free code generators are offered at no cost with no warranties.
- Paid (future), custom and supported code-generators will come with extra warranties about enforcing compliance by design.
- Need a custom code generator for your own stack? Feel free to reach us for a quote.
Credits
Build by Metadev from 2024 till 2026. All rights reserved.