Domain Driven Design, Transaction Script and Smart UI
Copyright 2014-17 Graham Berrisford.
One of about 300 papers at http://avancier.website. Last updated 28/12/2018 11:05
Any list of books on software design must surely include these two.
“Enterprise Application Architecture Patterns” Martin Fowler, 2002
· the Transaction Script pattern is better for simple logic
· the Rich Domain Model pattern is better when things get more complicated.
“Domain-Driven Design: Tackling Complexity in the Heart of Software” Erik Evans 2002
Evans focused on Domain Modelling, but he also mentioned patterns called Transaction Script and Smart UI.
This paper outlines the three different patterns.
An enterprise application enables or supports one or more business roles and processes.
It encapsulates some business rules, business knowledge or business capability.
A few enterprise applications are purely algorithmic, or need very little stored data.
But the majority maintain, or at least need access to, a substantial business data store.
Fowler says an enterprise application is typically composed of three horizontal layers, as shown below.
Client-side user interface
Controllers: handle requests/commands from the client
Views: populate views/pages sent to the client.
Models: retrieve and update data, applying business rules
A persistent data structure maintained using a database management system.
Typically a relational DBMS, but there are many data store varieties.
Dividing an application into three layers dates back to the 1970s - the days of COBOL and pre-relational databases.
The layers form a client-server hierarchy, meaning each layer requests services from the layer below
Despite the OOPers mantra that business rules belong in the middle later, many regard the data layer as the best place for data-centric business rules.
Middleware may be used to connect layers, but Fowler says "smart end points, dumb pipes", meaning don't put business rules in middleware.
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 for most enterprise applications may be seen from the client side and the server side.
Client side requirements
On the client side, the users require a user interface, usually divided into views.
The views are designed to what the user want to do in different use cases/sessions.
And within each use/case session, the user enters queries and commands the server-side application must respond to.
E.g. In one use case/session, one customer enters one order, with several order line/items, one each for one product to be ordered.
The customer sees to order-to-product relationship as 1 to N.
In another use case/session, one product manager enters a product number, and looks through the several order line/items for it.
The product manager sees to product-to-order relationship as 1 to N.
Server side requirements
On the server side, the back-end application must respond to the queries and commands.
In short term memory, it must remember what the user is currently doing in one session.
In long term memory, it must remember more persistent data – e.g. about products, customers, and all the orders they have placed.
The domain model
In domain-driven design, the domain model is shaped to fit the short-term memory of one use case/session
So bear in mind the domain model applies to a temporary cache of data on an app server, rather than the underlying database.
The persistent data structure of the long-term memory is usually defined in a logical data model.
A 1-to-1 association in short-term memory may represent only an extract or view of a 1-to-N association in long-term memory.
A 1-N association in short-term memory may represent only an extract or view of an N-to-N association in long-term memory
In other words, the “rich” domain model in DDD is a less complete representation of the “real world” domain than the logical data model.
This explains why some of Eric Evans’ examples appear careless in that the cardinality of association relationships is misleading.
Might DDD better be called View-Driven Design?
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).
DDD direct each event to the root entity in one aggregate.
Client-side user interface
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.