Transaction Script, Smart UI and DDD

 

 

Enterprise application design patterns include Transaction Script, Smart UI and Domain Driven Design (DDD). Which is right for you?

 

I drafted this article more than a decade ago. I was scared of being wrong then. And it might be out of date now. What do you think?

Abstract

Any list of books on software design is likely to include the following two.

 “Domain-Driven Design: Tackling Complexity in the Heart of Software” Erik Evans 2002

“Enterprise Application Architecture Patterns” Martin Fowler, 2002

 

Evans wrote of Transaction Script, Smart UI and Domain Modelling design patterns. Fowler said: Rich Domain Model pattern is better when things get complicated - meaning a core business function with complex business rules.

Transaction Script pattern is better for simple logic.

 

Agile development maxims include KISS and YAGNI. And “don’t write complex code where you can write simple code.” Microsoft recommends [DDD] be applied only to complex domains” (Wikipedia January 2017).

 

Transaction Scripts and Smart UI patterns can be easier to code and maintain. Where data-centric rules are invoked from several transactions or screens, you scan factor out data object-based modules. Where you need to publish events (for others to consume) and log events (for subsequent query and replay), Transaction Scripts can do that.

What is an enterprise application?

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.

 

A layered architecture

In layered architecture, each layer requests services from the layer below. Dividing an application into three layers dates back to the 1970s, long before OOP.

·       Client-side user interface: Typically HTML pages and javascript running in a browser on the user's machine

·       Server-side application: Controllers: handle requests/commands from clients. Views: populate views/pages sent to clients. Models: retrieve and update data, applying business rules

·       Data store: A persistent data structure maintained using a database management system.

 

Client side requirements

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.

 

Server side requirements

The back-end application must respond to queries for and commands on data. In short term memory, it must remember what the user is 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. Despite the OOPers mantra that business rules belong in the middle layer, some regard the data layer as the best place for data-centric business rules.

 

Messaging systems may be used to integrate applications on the server-side. Fowler says "smart end points, dumb pipes", meaning don't put business rules in middleware.

Transaction script

The queries and commands that a server-side application are often called transactions.

Commands are update transactions which maintain the integrity of data within a given data store.

 

“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 sub procedures.” Fowler

 

Fowler wrote that the Transaction Script pattern is OK for the 90% of transactions.

 

What about reuse of code? Fowler wrote of sub procedures. Designers factor out common subroutines shared by different transactions. Typically, such a subroutine accesses one entity or an aggregate entity in the persistent data structure. It may contain a single procedure applied to that entity. Or, it may be an “object-based” module that encapsulates an 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.” However, there is a domain model in the form of a logical data model and/or a physical database schema. A data abstraction layer may be introduced to translate between logical and physical schemas. Or else, object-based modules can be designed to encapsulate elements in the physical database schema.

Smart UI

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.

 

Advantages

        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 and then 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.

 

Disadvantages

        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.” Evans

Domain-Driven Design (DDD)

 

Data models v domain models

An enterprise application typically has both a long-term memory structure and several short-term memory structures. The persistent data structure is typically defined in a logical data model. The short term memory of each use case or session is stored temporarily in a cache on an app server.

 

In DDD, the domain model is of a short-term memory. Being an “object-oriented” model, it includes definitions of the behaviors that act on the data. In complex domains; it likely features inheritance of behaviors from super types in class hierarchies.

 

Why does the cardinality of relationships look misleading in some of Eric Evans’ example? Why may a 1-to-N association in a data model appear as a 1-to-1 association in a domain? Why may an N-N association in a data model appear as an 1-to-N association in a domain model?

 

Because the domain” is the domain of a use case completed by a user from their viewpoint. In one use case/session, a customer enters one order with several order lines, one for each product to be ordered. So the customer sees the order-to-product relationship as 1 to N. In another use case/session, a product manager enters a product number, and looks through order lines for it. So the product manager sees the product-to-order relationship as 1 to N.

 

Dividing the domain model

In essence, the layered architecture is

·       Client-side user interface: Use cases/sessions.

·       Server-side application: Commands and queries, Domain events, Aggregate entities.

·       Database server: Entities/tables.

 

The domain model is divided into aggregates, each affected by events specific to the entities in it.

 

Domain Event: A domain object that defines an event (something that happens). Command and queries direct each event to the root entity in one aggregate.

 

Aggregate: A collection of objects that are bound together by a root entity, aka aggregate root. The root entity 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.

 

The need for a data abstraction layer

Since an aggregate must be populated with data from the underlying data source, the aggregates in the domain model must be mapped to aggregates of entities in a data model or schema In DDD, the models are usually different, requiring a complex object-database mapping layer.

 

Where a transaction is 1 domain event

When a command arrives at the application server, a command handler starts a transaction, invokes a command operation on the root entity, and 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. It guarantees the consistency of changes made within the aggregate. It performs the business logic of the operation, which results in either success or failure. It can publish Events that record the result of business logic performed and/or data updated.

 

Where a transaction is more than 1 domain event

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 transaction, then extra “compensating transactions” may be needed to restore data integrity. Read my article on microservices for more.

Related patterns

 

Command Query Responsibility Separation (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.