Chapter 5. Transaction Script and Active Record

Business logic is the most important part of software. It’s the reason the software is being implemented in the first place. A system’s user interface can be sexy, and its database can be blazingly fast and scalable. Still, if the software is not useful for the business, it’s nothing but a technology demo.

In the first part of this book, we saw how important it is for all stakeholders to communicate in the ubiquitous language and to have a shared understanding of the problem domain. The system’s source code should also “speak” the ubiquitous language and be designed according to the business domain’s shared model. But how do we implement it in such a way?

As we saw in Chapter 2, not all business subdomains are created equal. Different subdomains have different levels of strategic importance and complexity. This chapter begins our exploration of the different ways of modeling and implementing business logic code. We will start with two patterns suited for rather simple business logic: transaction script and active record.

Transaction Script

“Organizes business logic by procedures where each procedure handles a single request from the presentation.”

Patterns of Enterprise Application Architecture1

A system’s public interface can be seen as a collection of business transactions that the consumers can execute as shown in Figure 5-1. These transactions can retrieve information managed by the system, modify it, or both. The transaction script pattern organizes the system’s business logic by procedures, where each procedure implements a request from the system’s public interface. Effectively, the system’s public operations are used as encapsulation boundaries.

Transaction script interface
Figure 5-1. Transaction script interface

Implementation

Each procedure is implemented as a simple, straightforward procedural script. It can use a thin abstraction layer for integrating with storage mechanisms, but it is also free to access the databases directly.

The only requirement procedures have to fulfill is transactional behavior. Each operation should either succeed or fail but can never be in an in-between state. If a process fails for some reason, the system should remain consistent—hence the pattern’s name, transaction script.

Here is an example of a transaction script that converts batches of JSON files into XML files:

DB.StartTransaction();
 
var job = DB.LoadNextJob();
var json = LoadFile(source);
var xml = ConvertJsonToXml(json);
WriteFile(destination, xml.ToString();
DB.MarkJobAsCompleted(job);
 
DB.Commit()

Applicability Context

The transaction script pattern is well adapted to the most straightforward problem domains, where the business logic resembles simple procedural operations. For example, in extract-transform-load (ETL) operations, each operation extracts data from a source, applies transformation logic to convert it into another form and loads the result into the destination store. This process is shown in Figure 5-2.

Extract transform load data flow
Figure 5-2. Extract-transform-load data flow

The transaction script pattern naturally fits supporting subdomains where, by definition, the business logic is simple — supporting subdomains. It can also be used as an adapter for integrations with external systems — generic subdomains, or as a part of an anticorruption layer (more on that in Chapter 9).

The main advantage of the transaction script pattern is its simplicity. It introduces minimum abstractions and minimizes the overhead both in runtime performance and in understanding the business logic. That said, this simplicity is also the pattern’s disadvantage. The more complex the business logic gets, the more it’s prone to duplicate business logic across transactions, and consequently, inconsistent behavior — when the duplicated code goes out of sync. As a result, transactions should never be used for core subdomains, as this pattern won’t cope with the high complexity of a core subdomain’s business logic.

The simplicity of the transaction script earned it a dubious reputation. Sometimes, the pattern is even treated as an anti-pattern. After all, if a complex business logic is implemented as a transaction script, sooner than later it’s going to turn into an unmaintainable big ball of mud. It should be noted, however, despite the simplicity, the transaction script pattern is ubiquitous in software development. The next three business logic implementation patterns that we are going to discuss in this and the next two chapters, all include a transaction script -- either implementing the business logic, or as orchestrator for other components carrying out the business logic.

Active Record

“An object that wraps a row in a database table or view encapsulates the database access and adds domain logic on that data.”

Patterns of Enterprise Application Architecture

Like the transaction script pattern, active record supports cases where the business logic is simple. Here, however, the business logic may operate on more complex data structures. For example, instead of flat records, we can have more complicated object trees and hierarchies, as shown in Figure 5-3.

A more complicated data model with one to many and many to many relationships
Figure 5-3. A more complicated data model with one-to-many and many-to-many relationships

Operating on such data structures via a simple transaction script would result in lots of repetitive code. The mapping of the data to an in-memory representation would be duplicated all over.

Implementation

Consequently, this pattern uses dedicated objects to represent complicated data structures: active records. Apart from the data structure, these objects also implement data access methods for creating, reading, updating, and deleting records—the so-called CRUD operations. As a result, the active record objects are coupled to an object-relational mapping (ORM) or some other data access framework. The pattern’s name is derived from the fact that each data structure is “active”; i.e., it implements data access logic.

As in the previous pattern, the system’s business logic is organized in a transaction script. The difference between the two patterns is that in this case, instead of accessing the database directly, the transaction script manipulates active record objects. When it completes, the operation has to either complete or fail as an atomic transaction:

public class CreateUser {
 public void Execute(userDetails) {
  try {
    DB.StartTransaction();
 
    var user = new User();
  user.Name = userDetails.Name;
    user.Email = userDetails.Email;
    user.Save();
  DB.Commit();
    } catch {
      DB.Rollback();
  throw;
      }
    }
  }

The pattern’s goal is to encapsulate the complexity of mapping the in-memory object to the database’s schema. In addition to the persistence responsibility, the active record objects can contain business logic, for example, validating new values assigned to the fields or even implementing business-related procedures that manipulate an object’s data. That said, the distinctive feature of an active record object is the separation of data structures and behavior (business logic). Usually, active records’ fields have public getters and setters that allow external procedures to modify the active record’s state.

Applicability Context

Since, essentially, an active record is a transaction script that optimizes access to databases, this pattern can only support relatively simple business logic such as CRUD operations which at most validate the user’s input.

Accordingly, as in the case of transaction script, the active record pattern lends itself to supporting subdomains, integration of external solutions for generic subdomains, or model transformation tasks. The difference between the patterns is that active record addresses the complexity of mapping complicated data structures to a database’s schema.

The active record pattern is also known as anemic domain model antipattern. Or, in other words, an improperly designed domain model. I prefer to restrain from the negative connotation of the words “anemic” and “antipattern.” This pattern is a tool. Like any tool, it can solve problems, but it can potentially introduce more harm than good when applied in a wrong context. There is nothing wrong with using active records when the business logic is simple. Furthermore, using a more elaborate pattern when implementing simple business logic will also result in harm by introducing accidental complexity. In the next chapter, you will learn what a domain model is and how it differs from active record.

Note: It’s important to stress that in this context “Active Record” relates to the design pattern and not the “Active Record” framework. The pattern name was coined in Martin Fowler’s book “Patterns of Enterprise Applications Architecture”. The framework came later as one way to implement the pattern. In our context, we are talking about the design pattern and the concepts behind it, not a specific implementation.

Conclusion

In this chapter, we covered four different patterns for implementing a system’s business logic:

Transaction script

This pattern organizes the system’s operations as simple, straightforward procedural scripts. The procedures ensure that each operation is transactional—either it succeeds or it fails. The transaction script pattern lends itself to supporting subdomains, with business logic resembling simple ETL-like operations.

Active record

When the business logic is simple but operates on complicated data structures, you can implement those data structures as active records. An active record object is a data structure that provides simple CRUD data access methods.

1 Fowler, M. (2002). Patterns of Enterprise Application Architecture. Boston, MA: Addison-Wesley Educational.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.36.147