Domain Driven Design, Transaction Script and Smart UI
Copyright 2014-17 Graham Berrisford.
One of about 300 papers at http://avancier.website. Last updated 17/01/2017 19:18
Any list of books on software design must surely include these two.
2002: In “Enterprise Application Architecture Patterns” Martin Fowler said the Transaction Script pattern is better for simple logic, and Rich Domain Model is better when things get more complicated.
2003: In “Domain-Driven Design: Tackling Complexity in the Heart of Software” Erik Evans focused on Domain Modelling, but also mentioned patterns called Transaction Script and Smart UI.
Many programmers would sympathise with the maxim: “don’t write complex code where you can write simple code.”
But at the same time, being bright, they get bored with what is simple and repetitive, and like to explore what is complex.
This may explain the space devoted to Domain-Driven Design (DDD) by Fowler and Evans.
To be brutal, a solution architect might well see minimising the amount of programming skill needed to maintain an application as a goal.
And that might mean avoiding DDD until it is demonstrably of benefit.
“Microsoft recommends that it be applied only to complex domains” said Wikipedia January 2017.
It seems likely that many (most?) enterprise applications are more easily designed and maintained using Transaction Script or Smart UI patterns.
You simply factor out data object-based modules where data-centric rules are invoked from several transactions or screens.
Transaction scripts can equally well publish Events (for others to consume) and log Events (for subsequent query and replay).
The domain model
DDD may be misinterpreted as having the objective of modelling the real world
However, the real world is infinitely rich and complex, and can be modelled in infinitely many ways.
So “modelling the real world” only becomes meaningful when particular data processing requirements are known.
The requirements are crystallised in the queries and commands the server-side application must respond to.
The domain model should be shaped to fit the data required by queries and the business rules applied by commands.
Bear in mind the domain model applies to a temporary cache of data on an app server, rather than the full persistent data structure.
So, some of Eric Evans’ examples appear careless in that the cardinality of association relationships is misleading.
E.g. 1-to-1 or 1-N association in one domain model may represent only an extract or view of a 1-to-N or N-to-N association in the “full” model.
This is ironic, because it means the “rich” domain model in DDD is a less accurate representation of the “real world” than a logical data model.
Dividing the domain model
Domain-driven design divides a domain model into aggregates, each of which are affected by events specific to that domain.
Aggregate: A collection of objects that are bound together by a root entity, otherwise known as an aggregate root.
The aggregate root guarantees the consistency of changes made to its members.
External/client objects can only access the root object (core entity); they cannot hold references to its member objects.
Domain Event: A domain object that defines an event (something that happens).
Client-side user interface
N commands and queries
When an aggregate is instantiated, it must be populated with data from entities/tables in an underlying data source.
So the aggregates in the domain model must be mapped to aggregates of entities in a logical data model or physical database schema.
Since DDD is usually used where the models are different, it has the particular disadvantage of requiring a complex object-database mapping layer.
Processing transactional commands that correspond to one domain event
When a command arrives at the application server, a command handler:
· starts a transaction
· invokes a corresponding command operation on the root entity
· commits or rolls back the transaction
The command operation on the root entity:
· plays a role akin to the control procedure in a transaction script
· guarantees the consistency of changes made within the aggregate
· performs the business logic of the operation, which results in either success or failure.
· can publish Events that record the result of business logic performed and/or data updated.
Processing transactional commands that correspond to two or more domain events
Where a transaction command spans two or more domain events, affecting two or more aggregates, life becomes complex.
If each domain event is coded as a discrete transactions, then extra “compensating transactions” may be needed to restore data integrity.
In short, DDD is best used when the domain is a core business function with inherently complex business rules.
It can over-complicate what could be a simple system.
The queries and commands that a server-side application must respond to may be called transactions.
Commands are update transactions which are (traditionally at least) expected to ensure and maintain the integrity of stored data.
“Most business applications can be thought of as a series of transactions.
Each interaction between a client system and a server system contains a certain amount of logic.
In some cases this can be as simple as displaying information in the database.
In others it may involve many steps of validations and calculations.
A Transaction Script organizes all this logic primarily as a single procedure, making calls directly to the database or through a thin database wrapper.
Each transaction will have its own Transaction Script, although common subtasks can be broken into subprocedures.” Fowler
Fowler wrote that the Transaction Script pattern is OK for the 90% of business database systems in which most transactions have simple logic.
And that “Common subtasks can be broken into subprocedures”
Designers optimise and reuse code by factoring out common subroutines shared by different transactions.
Typically, such a common subroutine accesses one entity or aggregate entity in the persistent data structure.
It may contain a single procedure applied to that entity by two or more transactions.
Or, it may be an “object-based” module that encapsulates an entity or aggregate entity, and so contains all procedures that access them
Erik Evans says “the Transaction Script separates UI from
application, but does not provide for an object model.”
Well… there is a domain model in the form of a logical data model and/or a physical database schema.
If the database schema differs from the logical data model, then a data abstraction layer may be introduced to translate between the two.
Or else, object-based modules can be designed to encapsulate elements in the physical database schema.
Evans proposed using what might be regarded as simpler pattern for simple UI-centric applications.
“Put all the business logic into the user interface.
Chop the application into small functions and implement them as separate user interfaces, embedding the business rules into them.
Use a relational database as a shared repository of the data.
Use the most automated UI building and visual programming tools available.
• Productivity is high and immediate for simple applications.
• Less capable developers can work this way with little training.
• Even deficiencies in requirements-analysis can be overcome by releasing a prototype to users andthen quickly changing the product to fit their requests.
• Applications are decoupled from each other so that delivery schedules of small modules can be planned relatively accurately, and expansion of the system with additional, simple behavior is easy.
• Relational databases work well and provide integration at the data level.
• 4-GL tools work well.
• When applications are handed off, maintenance programmers will be able to quickly redo portions they can’t figure out since the effects should be localized to the UI being worked on.
• Integration of applications is difficult except through the database.
• There is no reuse of behavior and no abstraction of the business problem
• Rapid prototyping and iteration reach a natural limit because the lack of abstraction limits refactoring options.
• Complexity buries you quickly, so the growth path is strictly toward additional simple applications. There is no graceful path to richer behavior.
If this pattern is applied consciously, a team can avoid taking on a great deal of overhead that is required to attempt other approaches.
The common, costly mistake is to build an infrastructure and use tools much heavier weight than are needed, or to undertake a sophisticated design approach that you aren’t committed to carrying all the way.
Most flexible languages (such as Java) are overkill for these applications and will cost you dearly; a 4-GL style tool is the way to go.
Remember, one of the consequences of this pattern is that you can’t migrate to another design approach except by replacement of entire applications, and that integration is only through the database. Therefore, a later attempt to use Java will not be helped very much by the use of Java in the initial development.
Don’t think you’re building a flexible system just because you’re using a flexible language.
CQRS and data replication
These work together because the CQSR pattern separates Command and Query application components.
This helps you to separate underlying data stores (between update and reporting) if required.
DDD and CQRS
These work together because both separate the processing of update Commands and Queries.
Commands trigger update transactions which act on aggregates in the Domain Model.
Queries can be done using a different and more efficient approach, like directly executing stored procedures through a thin API Layer.
DDD, CQRS, and Event Sourcing
Where these three are combined, the root entities in a Domain Model are responsible for:
· accepting input Commands (from a Command Handler)
· validating Commands
· applying Commands to data within the aggregate entity
· saving one or more Events (via transactions) in an event store.
But transaction scripts can equally well publish Events (for others to consume) and log Events (for subsequent query and replay).
And you don’t need DDD or CQRS to design microservices, separate database update transactions from queries, publish Events, or use Event Sourcing.
You might use “microservices” to maintain discrete data stores, but the usual trade offs apply.
Read “Microservices” for more.
All free-to-read materials at http://avancier.website are paid for out of income from Avancier’s training courses and methods licences.
If you find the web site helpful, please spread the word and link to avancier.website in whichever social media you use.