What is Domain Driven Design (DDD)? If you read this Wikipedia article you will see that it talks about DDD consisting of a number of high-level concepts and practices. It refers to a ubiquitous language meaning that the domain model should form a common language given by domain experts for describing system requirements. It talks about breaking the domain into its entities, value objects, aggregates, events, services, repositories and factories.
I have been designing and building enterprise applications for nearly 4 decades, and this is NOT the way that I do it. It was not until 2002 that I started using a language that had OO capabilities, and at that time the notions of Object Oriented Design (OOD) and Domain Driven Design (DDD) had either not yet been formulated, or were in their infancy and were not widely publicised nor generally accepted in the programming community. All that I new was that Object Oriented Programming (OOP) was a programming technique that involved using encapsulation, inheritance and polymorphism in order to increase code reuse and decrease code maintenance. If you read any articles on how to make effective use of these three fundamental concepts you will see such concepts as coupling, cohesion and dependency being mentioned. These ideas existed in other languages before OOP was invented, so they are not unique to OOP. OOP itself is not a programming technique which is totally different from what came before, it uses the same basic principles, but adds encapsulation, inheritance and polymorphism into the mix.
I only ever work in a single domain, that of the enterprise application. This is characterised as having a user at one end filling out electronic forms, a database at the other end in which the form data is stored electronically, and software in the middle which transports data between the two ends and applies any business rules. A large application may be comprised of a number of interconnected modules or subsystems where each deals with a particular part of the business, such as Order Processing, Inventory, Shipments and Invoicing. Each subsystem contains a number of user transactions which perform various actions on various database tables. Too many people seem to think that each of these modules is a separate domain which requires its own design process, but I strongly disagree. Each module requires its own database and its own business logic, but while each physical database has a different set of tables and columns they can all be accessed using the same SQL code, which leaves the major difference as being nothing more than the business logic.
The 3 components which I identified above - two ends and a middle - can be implemented using the 3-Tier Architecture (3TA) which has a Presentation layer for the User Interface (UI), a Business layer (sometimes known as the Domain layer) for all the business or domain logic, and a Data Access layer for communicating with the physical database. This can be merged with the Model-View-Controller (MVC) design pattern by splitting the Presentation layer into a Controller and a View, with the Model being in the Business layer. This produces an application architecture which is shown in Figure 1:
Figure 1 - MVC plus 3 Tier Architecture
As I have been building enterprise applications for over 3 decades I have utilised my programming skills to build a series of frameworks (one for each programming language that I have used) which provide as much reusable code as possible in order to streamline the development process. My frameworks were specifically designed to build transactions to maintain the contents of a database, and in order to speed up the development process each of these frameworks has contained a series of reusable components in order to perform common functions. My latest framework, written specifically for PHP, contains the following reusable components:
For those of you who do not understand the difference between an entity and a service, an entity has state or data which can be loaded, changed and extracted over time. A service on the other hand is stateless - data goes in, is processed in some way, then spat out without hanging around for later extraction.
This group of reusable components came about because my experienced eye kept seeing repeating patterns in the software which I was writing. An application is comprised of a number of user transactions (also called "tasks") which the user can select in order to process a unit of work, and each transaction/task touches the database in some fashion. Regardless of what information a database table may contain it can only ever be accessed using an SQL query which performs either a Create, Read, Update or Delete (CRUD) operation. Each user transaction/task therefore has behaviour in the form of a series of operations which are performed on one or more database tables, and it is possible for the same behaviour to be used on different tables. I encapsulated each of these behaviours into a group of reusable controller scripts.
Although each task also has its own screen which contains columns from its database table(s) I also noticed a lot of similarities in the structures of the various screens, and I was able to condense these similarities into a small group of reusable XSL stylesheets. I then combined each combination of reusable Controller script and reusable XSL stylesheet into a separate set of reusable Transaction Patterns. They are reusable by virtue of the fact that I can identify a database table, then combine it with any Transaction Pattern in order to construct a user transaction. My ERP application currently has 17 subsystems, 389 tables and 3,000 tasks which were constructed from 40 Transaction Patterns and 2 XSL stylesheets.
Too many of today's programmers are taught to design their applications by starting with the software, which sits between the UI and the database, and leave the database till last as it is considered to be nothing more than "an implementation detail". In my early programming days I worked with badly structured software and badly designed databases, but I saw the light when I was taught that for a database application to be really successful the database must be properly designed by following the rules of Data Normalisation and, courtesy of a Jackson Structured Programming course, that the software structure should be designed to follow the database structure as closely as possible. I personally witnessed the improvement that this combination of ideas made to both the initial software development and its subsequent maintenance, so I have absolutely zero intention of turning my back on a winning formula on the say-so of some wet-behind-the-ears greenhorn tenderfoot whippersnapper who doesn't know which way is up.
To me the reverse is true - the design of the database takes preference and I build user transactions around that database design. It does not matter what business domain is covered by the database, the data for that domain is stored in tables and columns just like every other domain, and those tables and columns can be manipulated with SQL queries just like the tables and columns for every other domain. Each table has its own structure which is made available to its table class, and any business rules which are specific to a particular database table can be added to that table's class file.
Because I build nothing but database applications, and because I have a framework which was specifically designed to both build and control the running of tasks which touch database tables, I don't have to waste time in designing the software to carry out those tasks. Each task can be implemented from one of the Transaction Patterns which are provided by the framework, and this removes a great deal of the leg work which is still carried out by my rivals. This is why my productivity is higher both for new development and subsequent maintenance.
Creating a new subsystem follows the same basic steps:
Note here that because so much work is provided by pre-built and reusable framework components the bulk of the developer's time can be spent in dealing with the most important part of the software in any application - the business rules. The developer doesn't have to design any software around those business rules, the framework creates the class files for each database table and the developer adds code into each class file to process the business rules.
Time after time I see OO tutorials which say that after a use case has been identified a class should be constructed with a method name specifically to execute that particular use case. I do not. All the method names that can be used by my Controllers when communicating with Models have already been predefined in my abstract table class which is inherited by every concrete table class. This simple practice allows any of my Controllers to be used with any of my Models. This is a prime example of loose coupling, which is supposed to be good. If I were to manually create a method in a Model to execute a particular use case then I would have to manually create a Controller to call that method on that Model. As that method would be unique to that Model then that Controller could be used only with that Model, thus making that Controller inextricably tied to that Model and thus not reusable with any other Model. This is a prime example of tight coupling, which is supposed to be bad.
Instead I create a task in the framework's database for each and every use case. Each task points to a component script in the file system which is very small as all it does is identify which Model should be used with which View and which Controller. This means that instead of custom method names I use generic names, such as:
|OO purists||effect on the database||the Tony Marston way|
|createProduct()||insert a record into the PRODUCT table||
$table_id = 'product'; .... require "classes/$table_id.class.inc"; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST);
|createCustomer()||insert a record into the CUSTOMER table||
$table_id = 'customer'; .... require "classes/$table_id.class.inc"; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST);
|createInvoice()||insert a record into the INVOICE table||
$table_id = 'invoice'; .... require "classes/$table_id.class.inc"; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST);
|payInvoice()||insert a record into the PAYMENT table||
$table_id = 'payment'; .... require "classes/$table_id.class.inc"; $dbobject = new $table_id; $result = $dbobject->insertRecord($_POST);
Each method call from the Controller to the Model will execute a series of methods which are hard-coded in the abstract table class as shown in this set of UML diagrams. Those methods with a "_cm_" prefix are defined within the abstract class but are empty, which means that when they are executed nothing happens. However, if any of these methods is copied from the abstract class to the concrete class then the copy in the concrete class will override, or be executed instead of, the original in the abstract class. If any code is inserted into the method in the concrete class then this code will be executed when that method is called.
In this situation I use a standard OO technique called subclassing. I create a subclass of the table class with a name such as
<table>_sNN where "s" denotes a subclass and "NN" is an incrementing number. This automatically inherits all the methods in the superclass but allows me to override any of those methods. Note that the subclass deals with the same table as the superclass, it just provides different business rules for that table. I then change that task's component script to point to this subclass. You can see working examples of this in the Data Dictionary subsystem of the framework where you will find the following class files:
dict_table.class.inchandles the standard processing for "dict_table".
dict_table_s01.class.incis used to import table details from the database schema into the dictionary database.
dict_table_s02.class.incis used to export table details from the dictionary database into the PHP application.
People should remember that the primary objective of a software developer is to develop cost-effective software for the benefit of the paying customer. It is *NOT* to follow a set of arbitrary or artificial rules created by fellow developers to promote their idea of "purity" or "perfection". I have encountered many of these rules in the past, and I have found that I can write better software by ignoring them. The only universal rule that I follow comes from a book called "The Structure and Interpretation of Computer Programs" which was published by H. Abelson and G. Sussman in 1984:
Programs must be written for people to read, and only incidentally for machines to execute.
Other rules that I follow are "Keep It Simple, Stupid" and "If it ain't broke don't fix it".
Domain Driven Design is used by those who don't understand how databases work and how database applications work. Object Oriented Programming does not require a radically different design process, it is merely a coding technique which is exactly the same as procedural programming except for the addition of encapsulation, inheritance and polymorphism. The 3-Tier Architecture is a proven architectural pattern for client-server systems which I have implemented in two languages, one OO (PHP) and another not (UNIFACE), and I have found that implementing it using a language which supports encapsulation, inheritance and polymorphism can provide great benefits. The important thing is how you implement those OO concepts to obtain the maximum advantages. A faulty implementation is unlikely to produce any advantages.
A database application is a database application however you slice it. It does not matter what sort of data is held in a database, it is all data which is held in tables and columns. I don't need to design an application subsystem based on the data and its business rules - the data is defined in the database schema and the business rules for each table are defined within that table's object in the business layer. I don't need to design each object in the business layer as each object is a database table with standard characteristics and operations, and everything which is standard is provided by a framework component. Each unit of work (user transaction) which the user can select, regardless of its simplicity or complexity, is provided by one of my standard Transaction Patterns. Business logic can be coded into each table class by using the hook methods which are provided. The code in the Presentation layer which allows each business object to communicate with the user follows a standard pattern and is provided by the framework. The code in the Data Access layer which allows each business object to communicate with the physical database is provided by the framework.
You should see from this that when building a database application it helps if you have a proper framework for building database applications as this can provide a huge amount of standard functionality out of the box. The developer therefore does not have to spend time in duplicating this functionality over and over again, instead he can spend his time working on the critical part of the application the "payload" as it were, which is the collection of business rules.
Here are some other heretical articles I have written on the topic of OOP:
© Tony Marston
1st March 2018