Generating code from UML can be very useful, although the usefulness of the result depends on what type of UML diagrams you use.
We’ve been part of several large projects where we used UML class diagrams to model the domain model, according to the DDD principles. Domain models (being OO persistable class models or Web service model) focus on structural patterns, i.e. how the different constructs (like entities, values objects, aggregate, etc.) relate to each other. For structural models we prefer UML class diagrams as we feel the convey information in a much better way than pure textual DSLs.
Visualization of relationships provides a better overview and is a good platform for communication.
Let’s take a simple example bidirectional associations – they’re simple to model but annoying to implement by hand:
In the diagram above we’ve stated that A knows about B and B knows about A. Simple, clean and intuitive.
When it comes to implementing this you need to keep these associations in sync. In code terms this means than when you write Java/.NET code like
a.setB(b); you also need to call
b.setA(a). This can get tedious and we all know that when code becomes tedious we start to make mistakes and forget to call set in the opposite direction and we by mistake do it on the wrong instances of
b. An effective code generator can generate code that makes sure that proper synchronization occurs.
UML Stereotypes and UML Profiles
By utilizing UML Stereotypes (which are packaged in UML Profiles) and good modeling conventions, you can raise the level of abstraction substantially by focusing on the intent of the model instead of focusing on the technical details.
Example and Problem
Say that you have a 1-1 association between a Person entity and his/hers Name (typically in the form of a Value Object). This is typically modeled as a 1-1 association like this:
In many applications it is relevant to keep track of the users name (active history/temporal pattern) over time. This can be model like this:
The problem with this is:
- It isn’t really very readable – a Person can have many Name’s and each has a StartTime and an EndTime. What does this say out a Persons actual name? Not that much. The problem is that we’ve mixed business and technical concerns.
- How do we convey that a Person at anytime can have only 1 Name? We could use OCL (Object Constraint Language), but in our opinion this is still an imperfect solution compared to using stereotypes.
In UML you can define your own Stereotypes, which are markers (which through Tagged Values can carry additional information) that you can place on all UML artifacts (like Classes, Attributes, Associations, etc.).
Through a simple stereotype called History we can define that something changes overtime while maintaining an important business rule, that a person can only have ONE name at any given time:
There’s nothing in UML that defines what History means, this is something that we as modelers define. This means that when you introduce a Stereotype you also need to define the semantics that apply, so that when people read your models they can understand the intention behind the stereotypes. This specification is typically done as written documentation with additional images to make the intention clear (like above).
Connection to Code Generation
How the History stereotype translates into code, isn’t really interesting from a domain perspective, but it’s very important from a development perspective. The simplicity of the stereotype SHOULD be transferred to the generated code using proper encapsulation, so that the link between model and implementation is kept in line with DDD best practices – a developer should use as little translation as possible when going from the visual model to the code.
If you encapsulate the history handling code well and give the developers a stable interface, you can (and should) insulate the them from the implementation details. This gives you have a lot of flexibility by allowing you to change the implement depending on needs.
The take away point is that the user of your generated code CAN and SHOULD be isolated from the technical details of the implementation. Also know as encapsulation
Examples of temporal pattern implementation for a 1-1 association includes:
A note: The generated code SHOULD remain free of algorithmic code (except code that handles or interacts with framework code that provides functionality for things such as bidirectionality, active history/temporal logic, versioning/temporal object pattern, etc.) to adhere to the principles of DCI (Data Context Interaction) – which states that domain models should be “dumb” and domain logic should be coded in Context (equivalent to Use Cases) such that Context logic doesn’t get spread around too many entities (in order to keep the entities coherent). Entities are then allowed to play different Roles depending on Context (so that you can capture a Person acting both as a Parent, Spouse, Customer, Employee, etc. – i.e. the Person is uniquely identifiable but can “transmorph” into different forms/roles depending on the Context). It is in the Roles that Entity Logic interplays with Domain Logic.
In case you WANT to add your own code to the generated code you can either apply the Generator Gap (will be presented in an upcoming blog post), Partial classes, Traits/Mixins, Priviledged Aspects, Protected Regions.
Next blog post will present how to create a flexible code generator will allow you to control all aspects of the code generation as presented here.