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, data transfer objects, aggregates, events, services, repositories and factories. The primary focus of this process is the domain logic, the business rules for that domain. Any logic which is not specific to a particular domain, such as communicating with the database or the user interface, is left until last as it is considered to be nothing more than an implementation detail.
I have been designing and building enterprise applications for nearly 4 decades, and this is NOT the way that I do it. The design and implementation of any software application has the following phases:
I analyse the business requirements in order to produce a logical design which identifies what needs to be done, and after this has been verified by the client I produce a physical design which identifies how it will be done. Then and only then can I begin the implementation. Some developers like to design the software components first and leave the database design till last as they consider it to be nothing more than an "implementation detail", but this usually produces what is known as an Object-Relational Impedence Mismatch which causes its own set of problems. To me it is the business logic which is the implementation detail and which can be left until last, so I prefer to design and build my database first, then create my software components to match the database structure, thus avoiding the possibility of any mismatch. This produces a separate class for each database table, but I use a framework which generates these classes for me. I recognised decades ago that in a database application each event/task will interact with one or more database tables and that the only operations which can be performed on a database table are Create, Read, Update and Delete. I also recognised that different events/tasks can sometimes have identical behaviour with the only difference being the database table(s) on which they operate, so I encapsulated this behaviour into a series of reusable standard templates known as Transaction Patterns. To create a new event/task I simply join a Transaction Pattern with one or more table classes, press a button, and the framework automatically creates a working task for me. That standard template will provide the basic functionality so that I can run the transaction and communicate with the database, and I add in the specific business rules afterwards by filling the relevant hook methods with the necessary code.
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 knew was that Object Oriented Programming (OOP) was a programming technique that was similar to procedural programming but with the addition of encapsulation, inheritance and polymorphism which could be used to increase code reuse and decrease code maintenance. I say could because the use of OO techniques does not automatically guarantee that the code will be better, the only guarantee is that it will be different. The result of using OOP is still down to the skill of the individual programmer. If you read any articles on how to make effective use of these three fundamental concepts you will see such ideas 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. This is discussed in more detail in What is the difference between Procedural and OO programming?
While Domain Driven Design concentrates on the unique aspects of each particular domain, which is fine for the Logical Design, the novice programmer may also assume that the Physical Design and the eventual Implementation should also be based around these unique aspects, but this again is an area where I choose to be different. Anyone who has ever built a large number of user transactions will be able to tell you that writing the code to implement the business rules is only a small part of the total code that will actually be required to execute those business rules. There is a lot of code which handles the communication with the client, the construction of HTML, CSV and PDF documents, the validation of user input, and the communication with the database. This is code which is not part of the domain, but which has to be written in order to get the application to work. Experienced programmers should be able to look at a number of different user transactions and see a number of similarities with this non-domain code. Not only will there be similarities in this non-domain code within the user transactions for an individual domain but these similarities will also appear in the transactions for other domains. For example, if your application has several domains which each communicates with a relation database, then the structure of the code which performs this communication will be identical regardless of the particular domain. While other programmers are being taught to start with the domain logic and add in the non-domain logic afterwards I have found great benefit in doing the exact opposite - I start with the non-domain logic and add in the domain logic afterwards.
According to Domain-Driven Design - What is it and how do you use it? the term "domain" can be defined as:
A sphere of knowledge and activity around which the application logic revolves.
Wikipedia has this to say on the subject of Domain Driven Design:
Of primary importance is a domain, the subject area to which the user applies a program is the domain of the software. A software's domain governs its context, the setting in which a word or statement appears that determines its meaning. From this, developers build a domain model: a system of abstractions that describes selected aspects of a domain and can be used to solve problems related to that domain.
Martin Fowler, the author of Patterns of Enterprise Application Architecture, has this to say on the subject of Domain Driven Design:
Domain-Driven Design is an approach to software development that centers the development on programming a domain model that has a rich understanding of the processes and rules of a domain.
Does this mean that if a particular piece of software covers a
different sphere of knowledge, or covers a different
subject area, or has different
processes and rules that it is said to cover a different domain and therefore require a different design process? When do you recognise that a piece of software is nothing more than an instance of an existing domain, but with some differences, rather than a new domain in its own right? This is an important question because if you don't come up with the right answer then you could spend a lot of time in travelling down the wrong path, which would be a waste of both time and effort.
For example, I have developed a large ERP application which currently caters for the following different areas of business:
Each of these different areas (which I call subsystems) has its own database, its own set of inputs and outputs, and its own set of business rules. They are integrated by virtue of the fact that data can be shared. For example, the ORDER subsystem references the PARTY subsystem for details of customers or suppliers, and references the PRODUCT subsystem for details of products and services which can be bought or sold. It would be very inefficient if the ORDER subsystem had its own tables for customers, suppliers and products.
While there are differences there are also a large number of similarities, it is the volume of similarities which tells me that rather than each of the above being a separate domain they are in fact just different instances of the same domain. I am only concerned with the building of web-based database applications for the enterprise and regardless of what data is being processed the architecture of each subsystem is identical in that they all have components which exist in the Presentation layer, Business layer and Data Access layer. I would classify the following as being in totally different domains:
I have never worked in any of those areas so I wouldn't even know where to start, but I have worked with database applications for over 40 years, so it would fair to say that my expertise with such applications is way above average.
When a novice programmer first examines different business areas covered by an enterprise application a casual glance will immediately highlight the differences:
That novice will then assume that because each area is so different from the others that it will require a totally different design methodology and therefore totally different code to implement that design. However, when you have built as many database applications as I have you will quickly learn that the differences actually hide a number of similarities which provide opportunities for significant amounts of reusable and sharable code. An experienced programmer will tell you that the more reusable code you have then the less code you have to write. This leads to shorter development timescales, lower costs, quicker time-to-market (TTM) and easier maintenance.
But where are these similarities I hear you ask? Where are the opportunities for reusable code? As noted previously I have developed a large ERP application which contains a number of different domains which have their differences. However, an experienced developer who has worked on several domains will be able to recognise that each domain will contain a mixture of code which is totally unique as well as other code which is similar if not identical to that which appears in other domains. If you read Anatomy of an Enterprise Application you will see that, regardless of the domain, each user transaction follows the same pattern - there is a User Interface (UI) at the front end, a database at the back end, and software in the middle which transfers data between the two, and which applies the business rules. There are basically two ways in which you can view this problem, and your design of the solution will be heavily influenced by which view you take:
By being able to identify the patterns and rules which apply to each of the above areas an experienced programmer should be able to produce the boilerplate code to handle the common processing. A really clever programmer should be able to go one step further and place this common code into reusable routines so that each routine can be called many times instead of being duplicated many times. An expert programmer can go even further by producing code templates for each of the recognised patterns, then combine these into a framework which is capable of building skeleton programs which contain all the standard processing into which the code for handling the unique business rules can be inserted. In the RADICORE framework these patterns are provided as pre-built and reusable pieces of code as follows:
So there you have the two alternatives - start with the unique code for the business rules and then add in the boilerplate code for all the boring and repetitious parts, or start with the boilerplate code and then add in the unique code. While the latter choice requires more up-front effort than the former, if you are in the business of developing database applications which expand to encompass a growing number of business domains then the investment in effort will pay dividends in the long run. Using the components in my framework I am able to build new transactions which contain all the standard boilerplate code very quickly and with little effort. All I then have to do is add the necessary custom code into the relevant hook methods in order to implement the unique business rules. To put it another way, the framework helps me build working but basic skeleton programs which I then flesh out with business rules.
The three processing areas which I identified previously have been known about for a long time and have been described in an architectural pattern known as the 3-Tier Architecture (3TA) which has a Presentation layer (front end) for the User Interface (UI), a Business layer (also known as the Domain layer) for all the business/domain logic, and a Data Access layer (back end) for communicating with the physical database. Another common pattern is the Model-View-Controller (MVC) which I have merged with 3TA 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
The following areas have similar characteristics which provide opportunities for reusable code:
Creating CSV output is the easiest - simply extract the array of data from the Model component and write it to a disk file. I do not have to know what data this array contains, so I can extract different arrays of data from any number of different objects and the process of creating a CSV file from this data is exactly the same.
Another common mistake that a novice programmer often makes is to consider each event/task in a domain as a series of business rules which incidentally touch the database. I learned a long time ago that the best approach is to take the opposite view - each event/task is mainly concerned with accessing one or more database tables using one or more of the standard CRUD operations, and only incidentally executes a group of business rules. This is why I always start with the standard code and add in the business rules later. In this way I have been able to incorporate a large number of instances of the Template Method Pattern into my framework. This utilises an abstract class which contains the numerous template methods and invariant methods which contain the standard code, and a collection of "hook" methods which can have different implementations in each subclass in order to handle the business rules which are specific to that subclass.
When you look at the four types of component in Figure 1 you should now be able to see how I provide them in my code:
Each user transaction - and there may be hundreds, or even thousands - will therefore contain a mixture of code which falls into one of the following categories:
I have been building enterprise applications for nearly 4 decades, and my approach has always been to start building the components around the domain-agnostic logic and insert the domain-specific logic later. Why? Because in my experience the volume of domain-agnostic logic is far greater than the volume of domain-specific logic, and as there are large volumes of domain-agnostic logic which can be provided in reusable and sharable modules I have found it easier to build each user transaction by starting with the domain-agnostic logic and adding in the domain-specific logic later. Neither the front-end Presentation layer nor the back-end Data Access layer should contain any domain-specific logic, and the middle Business/Domain layer will always contain a mixture of standard domain-agnostic logic (the invariant parts of a Template Method) and non-standard domain-specific logic (the variant/variable/customisable methods in each subclass). With my technique I can build a raw user transaction from a template and add in the domain-specific logic afterwards at a faster rate than you can by starting with the domain-specific logic and adding in the domain-agnostic logic later.
In an enterprise application each user transaction has some sort of interaction with the database which, regardless of the table and the data it contains, follows a familiar and regular pattern. Each HTML document, while superficially different, has enough in common with other documents to enable it to be built from one of a small number of reusable XSL Stylesheets which implement a series of reusable modules called templates. Each Controller, which translates user requests into actions on a particular Model, can use the same pattern of actions on any number of different Models, so can be provided as a library of pre-written and reusable components.
The key to successful software development is the ability to spot these recurring templates and patterns, then to provide them with as much pre-written and reusable code as possible. This is the basis on which I developed my series of development frameworks. As far back as 1985 I built my first development framework in COBOL to provide as much domain-agnostic and sharable code as possible. In the 1990s I rewrote this in the UNIFACE language, and in 2003 I rewrote it again specifically for PHP. With each rewrite the additional capabilities of each new language allowed me to build additional features into my development framework and also to provide more reusable components as well as shorter development times. My latest PHP framework allows me to generate working user transactions which automatically contain all the necessary domain-agnostic code. The developer can then add in the necessary domain-specific code without having to touch the standard code which is provided by the framework. This is an easy job as the abstract table class, from which every concrete table class inherits, contains a series of hook methods specifically for this purpose.
In the introduction I identified the various components which are supposedly "required" when implementing DDD, but I have found a use for only some of them:
Note that in my application the only "grouping" I perform with database tables is to have a separate database for each domain. With modern RDBMS software once you have opened a connection to a database server you can access any number of databases in that server, even within the same SQL query.
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. An example is a database record which has its current values extracted from the database, displayed in the UI where changes may be made and posted back, which causes the changed values to be updated in the database. A service on the other hand is stateless - data goes in, is processed in some way, then immediately spat out without hanging around for later extraction or manipulation. An example of a service is an XSL Transformation which, after having transformed an XML document into an HTML web page, simply dies as there is nothing else left to do. The XML document is not retained for later use, it is discarded. Domain logic should only exist within an entity. A service should only contain logic which is required for the provision of that service and which is not specific to any domain.
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 course on Jackson Structured Programming, 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 no intention of turning my back on a winning formula. Just because someone has developed a different approach does not mean that I should follow it.
To me the reverse is true - the design of the database takes preference for the following reasons:
This approach falls into line with the following statement:
Smart data structures and dumb code works a lot better than the other way around.
It does not matter that the data and business rules for each business domain are totally different when the common factor across all domains is that the data is stored in the database as tables and columns which are manipulated with SQL queries using just 4 basic operations - Create, Read Update and Delete. Each table is treated as a separate object in the database, so I see no reason why each table should not have its own class/object in the application software. Each table contains certain business rules which are implied in its structure, so it is not unreasonable for the table's class to know its structure so that it can validate all input data against this structure. I particularly avoid the idea of object aggregation where a single object in the software is responsible for multiple objects (tables) in the database. This, in my opinion, would be a violation of SRP. In the real world the concept of an ORDER may exist as a single entity, but when it appears in the database after normalisation it is split into separate ORDER_HEADER and ORDER_LINE objects. Each of these is a separate object with a separate structure and business rules, so each deserves its own separate table class in the software.
All the code which is common to every database table I put into an abstract class. Each table has its own name and collection of columns, so I specify these particulars in the class constructor which loads data from the table structure file and turns it into a specific concrete class. While the collection of columns in a table's structure may be unique, their data types always come from a predefined list, so the validation of user input which checks that the value for a column matches that column's data type can also be predefined. Each table has its own primary key, optional unique keys and relationships with other tables, so these can also be identified in the class constructor and handled by code which is inherited from the abstract class. Primary data validation is carried out by a standard validation class which compares all input data with that table's field specifications. This means that the developer does not have to write any code to perform this task, it is automatically handled by the framework.
The addition of specific business rules for particular tables is handled using the Template Method Pattern whereby the abstract table class contains a series of "hook" methods which are empty by default. If a developer wishes to add custom code into a particular step of the processing cycle all he has to do is copy the relevant empty "hook" method from the abstract class into his concrete class, then insert the necessary code into this method.
This means that to deal with a new domain with a new database all I need do is follow these steps:
I do not need to build any Controllers or Views as these are provided by the framework as pre-built and reusable components. Building user transactions is as simple as selecting a database table then joining it with one of the Transaction Patterns which is also built into the framework. This is explained in Use the framework to build components.
When I started my programming career with COBOL in the 1970s a common complaint was that the language was so verbose with its numerous divisions (identification division, environment division, data division and procedure division) that it took a long time to write even a simple program. Then a person much older and wiser than me pointed out a simple fact - a good programmer need only ever write one program from scratch, after which he could copy this code and amend it as necessary to create a different program. As COBOL was a language designed for business applications, and all these applications used some sort of database, there was a great deal of code which moved the data from the database to the screen and from the screen to the database by constructing and executing queries. Note that SQL did not exist at that time, so a "query" was actually a hard-coded function call that used the non-relational database that was provided with the operating system. The differences between one program and the next were then limited to the name of the database table and the names of its columns, plus the business rules which were unique to that table. My goal then became to write code in such a way that it was easy to identify that which was similar from that which was different so that I could minimise the changes necessary to make the code work with a different table and its different set of columns. Note that I use the word "similar" and not "identical". Each program may have a block of code which moves data from the database to the screen, and another block which moves data from the screen to the database, but each block contains a series of
MOVE source TO destination statements where the "source" and "destination" names were hard-coded and could not be parameterised.
All I could do initially was to put identical code into a shared library so that the library function could be called instead of the code within that function being duplicated. I also began to write library functions which dealt with "similar" conditions by passing the differences as arguments so that the function body could be kept as static as possible. The results of this work is identified in Library of Standard Utilities on my COBOL page.
As explained in What is a Framework? you have to write your own code in order to call a library routine, whereas a framework contains components which call your code by following the Inversion of Control (IoC) principle which is implemented using the Template Method Pattern. A framework should also allow you to create application components by writing less code.
I converted my library into a framework while working on a project in 1985. Up until that point the practice had been to hard-code each of the menu screens so that the user could see a list of options and choose one to be executed. A menu had to fit within the screen's dimensions (no scrolling was allowed), so each menu page had to be written as a separate subprogram. Note the difference between a "program" and a "subprogram" - a program can only be activated from the command line, while a subprogram can only be called from within a program or subprogram. The starting point for each application was always a logon screen which accepted a user ID and password, and from then on it passed control to the top-level menu where it waited for the user to choose which option to run next. Each application therefore had its own copy of the logon screen, its own set of hard-coded menu screens, plus its own set of application components. Not all users had access to all components, so there was a primitive mechanism which identified who could access what. Unfortunately this condition could only be evaluated after the component was loaded, so while all users could see all options on the menu screen, they sometimes selected an option only to be told "You don't have access to this option".
This all changed when the customer made a simple request: "If a user does not have access to an option, then that option should not appear on the menu screen". This meant that I had to change from using static menus to a system of dynamic menus. The request was made on a Friday, so I spent a couple of hours over the weekend in designing a solution and started implementing it on the Monday. By the following Friday the implementation was complete. The solution involved the creation of a new set of database tables plus the programs to maintain them. These are documented in the Menu and Security System User Manual as follows:
This arrangement allowed menu screens to be built or modified by changing the contents of a few database tables without having to change any program code. When a menu option was selected the contents of the D-MENU table was read, and any options which were not on the D-TRAN-USER table for that user were filtered out. This meant that the menu screen never displayed an option which the user could not select. This also removed the need to have any security checking within any subprograms as such checking was now performed by the framework before the subprogram was called instead of within the subprogram after it was called. This software became a framework by virtue of the fact that it provided a standard set of pre-written code which could be reused in any application without having to be redesigned and rewritten each time. The programmer no longer had to write any code to call his application components as the standard code did this all for him. This meant that the programmer could concentrate on writing the application components and not waste time on the code which called those components.
This database structure was carried forward when I redeveloped the framework in each new language, first UNIFACE in the 1990s and then PHP in 2003. Each of these languages came with its own set of new facilities which either allowed more options to be provided, or allowed more code to be transferred to reusable functions. The results of the rewrite in PHP have proved to be very successful as I am now able to build application components at a much faster rate and with more features built in as standard than I could with its previous incarnations.
The challenge with PHP was that it was the first language I had used which had Object Oriented (OO) capabilities, so I had to learn how to use these things called Encapsulation, Inheritance and Polymorphism to maximum advantage. According to some people my implementation of these principles is totally wrong, but their arguments are futile when you consider that my results are superior to theirs.
The steps I took to build the components to maintain the first database table in my framework are detailed in Building the first components. This resulted in the creation of 1 Model class and 6 Controllers.
I then copied these 7 scripts and renamed them to perform the same actions on a second database table, as described in Duplicating and Refactoring components. As this resulted in a huge amount of duplicated code I then went through a refactoring exercise in order to put as much common code as possible into reusable modules. For the two Model classes I created an abstract table class for the common code which could be inherited, then moved code from the table class into the abstract class. When I was finished I had nothing left in each table class except the constructor which did nothing but supply the table name and its structure.
Then I turned my attention to the Controllers. Instead of having a separate Controller which contained hard-coded references to a particular Model class I found a way to make them work with a class name that was not supplied until run-time. This mechanism is called a component script, and there is one of these for every user transaction in the application. If you study my implementation carefully you should see that each Controller is dependent on a Model (sometimes two or three models), but rather than this dependency being hard-coded it is in fact injected at run-time, thus demonstrating a form of Dependency Injection. Because the methods called by each Controller are defined within the abstract table class which is inherited by every concrete table class this means that each Controller is capable of calling the same methods on each of the 450 table classes in my enterprise application, thus demonstrating one of the goals of OOP which is called Polymorphism.
Over time I discovered that some user transactions were becoming more complicated than could be covered by my original 6 Controllers, so I gradually added to their number until I now have over 40 of them which are documented in Transaction Patterns for Web Applications
Initially I had to generate the scripts for new table classes and user transactions by hand, but as all these followed a standard pattern I decided to automate them as much as possible. To handle the class files I followed the steps described in Generating class files. To handle the user transactions I followed the steps described in Generating user transactions. This then meant that I could create a new table in my database, then generate and run the 6 basic user transactions in 5 minutes without writing a single line of code - no PHP, no HTML, no SQL. All the domain-agnostic code is provided by the framework while it is only the domain-specific code which needs to be written by the developer. This is why my productivity is higher than my rivals both for new development and subsequent maintenance.
Creating a new subsystem/domain 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 the relevant hook method in 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. For example, in Get your feet wet with domain-driven design: 3 guiding principles it says the following:
When you begin to embed the domain model in your code, it's kind of obvious that you should name classes the same as their real-world counterparts, but it's not so obvious that you should also name methods the same as their domain-level operations.
For example, if the domain expert says that at a certain point in the application the user can sign up for a subscription to a magazine, please don't name the method that implements that functionality "CreateCustomer." Instead, name it as specifically as you can, in domain terms. Imagine that the domain expert is going to look at your code one day. Would she understand the gist of it from the interface, i.e. from the class and method names? Instead of "CreateCustomer," perhaps "SignUpForMagazineSubscription" would be a better name.
In Domain-driven design it says the following:
Domain-driven design (DDD) is the concept that the structure and language of software code (class names, class methods, class variables) should match the business domain. For example, if a software processes loan applications, it might have classes such as LoanApplication and Customer, and methods such as AcceptOffer and Withdraw.
I do not think this way, and I do not write code this way. I have been writing database applications for over 30 years, and I have learned to start with the database design and build my software around the structure of that database. This means the following:
This methodology provides the following benefits:
Instead of using method names which are unique to the operation within each domain I use names which are common to the underlying database activity that will be performed during the execution of that use case. All the method names that can be used by my Controllers when communicating with Models have already been predefined as public methods 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 of creating a separate and unique method for each use case I create a separate and unique record in the MNU_TASK table in the framework's database. The task identity is in the format
|ppp||is a prefix which is unique to each subsystem|
|<table>||is the table name|
|<pattern>||is the identity of the Transaction Pattern|
|sss||is an optional suffix, used when a table has more than one task of the same pattern|
Note that the script_id which is run when a task is executed is not the same as the task_id. This is to allow the same script to be used for different tasks where the behaviour of the script can be altered by using any of the task parameters.
The script_id, which is usually in the same format as the task_id but without the prefix, will exist as a file, called a component script, in that subsystem's root directory. This will be a small file which looks similar to the following:
<?php $table_id = "person"; // identify the Model $screen = 'person.detail.screen.inc'; // identify the View require 'std.enquire1.inc'; // activate the Controller ?>
Note that there is a different controller script for each Transaction Pattern, and each Transaction pattern is capable of being used with any table in the database.
This means that instead of custom method names I use custom component scripts which reference pre-built and reusable controller scripts which, in turn, use the generic methods which are defined in the abstract table class which is inherited by every concrete table class. This means that I can generate a new task which performs a particular pattern of behaviour on a table with the absolute minimum of effort, as shown below:
|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);
A significant point to notice here is that I do NOT have a separate Controller for each Model which calls methods which are specific to that Model as this would require each Controller and Model to be hand-crafted and tightly coupled to each other. None of my Controllers is tightly coupled to a Model, and none of my Models is tightly coupled to a Controller. All my Controllers are supplied by the framework as services, and each Model is generated by the framework as an entity. All my Controllers access their Model(s) by using method names which have been defined in my abstract table class, and each Model inherits from this same abstract table class. This means that no Controller is inextricably tied to a single Model, which would produce tight coupling, but instead any Controller can be used with any Model, thus becoming as loosely coupled as is possible.
Anybody who knows anything about OOP should see that my method is a shining example of polymorphism in action. Every one of my table classes uses the same method signatures by virtue of the fact that they are provided by the same abstract table class. Every one of my page controllers calls these methods, so if I have 40 controllers and 450 table classes that means that I have 40 x 450 = 18,000 - yes EIGHTEEN THOUSAND - opportunities for polymorphism. How many frameworks have you seen which can match that?
By creating a separate database record for each task instead of a separate method name I am then able to provide the following facilities by adding records to other database tables instead of writing additional code:
When I was a junior programmer it was common practice to create a single program for a database table which then handled all the different modes of access - list, add, read, update, delete and search. Controls were included in each screen which allowed the user to switch from one mode to another. Problems began to arise when it became necessary to add in user access controls so that a user's access to particular modes with a program could be turned on or off. It then became necessary to add code into each program to check the user's Access Control List (ACL) so that when the user requested a mode to which he/she did not have permission to access an error message was displayed. This started to become cumbersome and the cause of much frustration, so as soon as I had the power to change the design I switched from a single large program handling all six modes to six small programs each of which handled a single mode. This led to the series of tables described above which means that all access control is handled by the framework and requires no application code to be written by the developer.
I have often been criticised for having a separate class for each database table, which must mean that some programmers out there are constructing classes which deal with groups of tables. I have also been criticised for having the same Model being accessed by multiple Controllers, which must mean that some programmers out there are constructing a separate Controller for each Model which handles all the tasks (use cases) for that Model. Both of these ideas strike me as being utterly ridiculous as they completely obliterate certain ways in which code can be reused. As the objective of OOP is supposed to increase the amount of reusable code so that the developer can create cost-effective software by writing less code I consider any technique which does the opposite to be an anti-pattern.
Take as an example the collection of tables which exist in the ORDER subsystem of my ERP application as shown in Figure 2:
Figure 2 - a compound "order" object
Now imagine the effects of creating a single compound object to handle those 11 tables and creating a single Controller to handle every one of the six tasks which would be required to operate on each of those tables. By my reckoning that would need 11 x 6 = 66 unique method names, each of which would have to be crafted by hand.
My method requires far less effort as it makes maximum use of pre-written and reusable code.
Notice how many patterns I use and how many times I use them:
The LIST1 pattern can be activated from a menu button. The LIST2 pattern can be activated from a navigation button within the LIST/2 screen for the task which deals with the parent/outer entity. The remaining ADD, ENQUIRE, UPDATE, DELETE and SEARCH tasks can be activated from a navigation button within the LIST1/2 screen for the same family.
While I can create an entry on the ORDER_HEADER table at any time I cannot create entries on the other 10 tables without specifying the foreign key to its immediate parent. Note that a valid order need only contain one or more order items, all the other tables are entirely optional.
While the ORDER_HEADER can be regarded as the root I can access any of the other tables directly without having to go through the root. I do not create a single class to handle all those tables as each table has its own class. I do not create a single controller to handle all the use cases as each use case has its own reusable controller. I could also add another table into this group without having to amend any existing members of the group.
The observant among you may then to able to recognise those chunks of code which I DON'T have to write.
By having such a large amount of reusable code at my disposal I am therefore able to create software components with less effort and at a faster rate. This makes me more productive than most other developers and my software more cost-effective.
An aggregate object is one which contains other objects. This is a result of encountering a whole/part (or "has-a") relationship between several objects. An example of this is shown in Figure 2 above where an ORDER object is actually comprised of several smaller objects. Whereas a programmer who has been trained in "proper" OO techniques would see no problem in creating a single aggregate object to contain all those component objects, I personally would never dream of doing such a thing. As a heretic I do the worst thing possible by avoiding aggregate objects completely in favour of having a separate class for each individual component.>
But does my heretical approach actually cause any problems? Absolutely not. The biggest problem I have seen with aggregate objects is that when you instantiate one some programmers seem to believe that you must also instantiate each of the component objects and, even worse, populate them with data. I remember years ago reading a post in a newsgroup where a novice programmer complained that his software, even after having been built using all the "proper" methods, as he had been taught, was running very slow. He had built a small system to handle data for his school which handles such entities as teachers, students, subjects, lessons and classrooms. When he fired up his application he instantiated an aggregate "school" object which immediately instantiated all the component objects which in turn loaded all their database data into memory. He could not understand what he had done wrong.
This simple answer is that he had done what he was taught to do, but did not have the experience to realise that what he had been taught was nothing more than a pile of manure. Having been involved in the designing and building of database applications for over 20 years I was utterly amazed and appalled that anyone could even dream of adopting such a ridiculous approach. As I had already built my own Prototype Classroom Scheduling Application I was very familiar with the requirements of a school system, and I was also very familiar with the most efficient and effective way to satisfy those requirements. I have a separate use case for each view of the data, and I only load into memory that data which is actually required for that use case. What could possibly be more efficient than that?
If you show the average programmer a collection of different HTML pages they will tell you that each is totally unique and therefore needs to be designed and built independently. An experienced programmer should be able to see the similarities as well as the differences. The similarities are elements which appear in multiple pages and which look and act in the same way, and it should be possible to create a catalog of patterns which show the different arrangements of these similar elements. Among these similar and repeating elements are the following:
Long ago, after having designed thousands of different forms, I recognised that each screen/form could be described in the following ways:
Unlike with my previous languages which required the forms to be built and compiled using a built-in process, the building of HTML pages is far easier simply because each page is constructed as a text file which does not have to be compiled and therefore can be constructed on the fly. This text file can be constructed in many ways, but my method of choice was to put all the relevant data into an XML document and then transform it into HTML using an XSL stylesheet. I started off with a separate XSL stylesheet for each HTML page, but following a process of refactoring I managed to reduce this down to a small set of Reusable XSL Stylesheets. I created a single reusable View object to handle both the construction of the XML document and the XSL transformation, which then meant that I did not have to write any code, apart from the small screen structure script, in order to create any web page.
Some pages produce PDF output instead of HTML, so again I have a single View object which handles all the processing with the aid of a small report structure file.
Some of the business rules are implemented in the structure of the database which identifies what data elements need to be stored for each entity. Each of these elements has a specific type and size, and can be designated as being either NULL (optional) or NOT NULL (required). As each table class contains a $fieldspec array which identifies all the columns which belong to that particular table, along with their specifications, a competent programmer should be able to create a standard routine which validates that each piece of user input matches the database definition before any attempt is made to add it to the database. In my framework this functionality is provided by my validation class, so it is an example of more code which does not have to be written by the developer.
Each method call from the Controller to the Model will initiate a database operation in a series of predefined steps which have been built into the abstract table class as shown in this set of UML diagrams. Those methods with a "_cm_" prefix, which are known as customisable methods or "hook" methods, 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 empty 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.
The use of these "hook" methods is a prime example of the Template Method pattern.
It is quite possible that it some circumstances you may wish to perform some processing in a particular hook method for a particular task that is totally different from the code used in the same method for a different task. 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 with a different implementation. 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.
Before you leap into this thing called Domain Driven Design you should first ask the question What is a "domain"? If your answer is wrong then your implementation of that answer will also be wrong. I have written a large ERP application which contains over 20 subsystems. While some people regard these as different domains because their data is different and their business rules are different, I regard them as nothing more than instances of the same domain which is why I refer to them as "subsystems". They are all parts of the same web-based data processing system as they all follow the same pattern which is reflected in the 3-Tier Architecture - they have HTML documents in the Presentation layer, a relational database in the Data Access layer, with software in the Business layer to take care of the business rules. They each have different databases and business rules, but what are the similarities? Look at the following:
When I say that I don't do Domain Driven Design for each subsystem I mean that I don't need to design the software for each subsystem as it always involves the same mixture of components - Models, Views, Controllers and Data Access Objects. All I need to do for each subsystem is to design and build the database and identify the business rules. I don't need to design the Views, Controllers and Data Access Objects as these are already built into the framework. I don't need to design any Model classes as they can be generated from my Data Dictionary after the table structures have been imported from the database schema. Each database table will have its own concrete table class which inherits all its standard methods from an abstract table class which again is built into the framework. The use of an abstract table class means that I can use the Template Method Pattern so that I can add any extra business rules to a concrete table class by inserting code into any of the predefined "hook" methods.
The software used within each subsystem is comprised of two types of logic - domain-agnostic and domain-specific. The domain-agnostic logic does not contain any business rules for any domain, which means that it can work with the data for any domain without causing any problems. This logic can therefore be supplied by the framework. The domain-specific logic is built into the class files for that domain which exist in the Business/Domain layer, and these are the only classes that need to be built and maintained by the developer.
When I started to redevelop my framework using PHP I did not follow these things called "best practices" and "approved programming principles" simply because I did not know that they existed. There was no mention of them in the PHP manual, so I simply wrote my code using the facilities which the PHP language made available, namely encapsulation, inheritance and polymorphism. I was able to build a much larger collection of reusable code than I had done previously using COBOL and UNIFACE, which meant that I could create new components at a much faster rate, so I judged my efforts to be a success. Upon being informed that my work was totally wrong simply because I was not following the same rules as everyone else, I took a look at these rules but I decided that by trying to follow them I would be destroying what I had already achieved. It became obvious to me that the people who wrote these rules had little or no experience of developing web-based database applications, so their rules could only be applied in the domain for which they were written and were irrelevant for the domain in which I worked.
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".
The differences between the "approved" DDD approach and my heretical approach can be summarised in the following comparison matrix:
|the "approved" way||the Tony Marston way|
|1||Start by focusing on the domain logic and design the software around that logic.||No. After analysing the requirements I have a preliminary database design and a list of events/tasks (use cases). I then build the database and design the software based on actions that will be performed on those tables.|
|2||Design classes which have a unique method/operation for each use case.||No. If you have a large number of unique method names then you have fewer opportunities for code reuse via polymorphism, so the more method names which are identical the better. Each event has a separate task in the MENU database which points to a separate component script which in turn identifies a Model, View and Controller which will be used to implement that task. Every Controller communicates with its Model(s) using standard method names which every Model inherits from the same abstract table class.|
|3||Use the "IS-A" test to design class hierarchies.||No. The way that the "IS-A" test is applied by OO programmers to create layers of subclasses to deal with minor differences in data is simply not done in a database. Such differences may be represented as different rows in a single table instead of separate rows in different tables, so where I have only one table in my database I have only one corresponding class in my software. Please refer to Using "IS-A" to identify class hierarchies for more details.|
|4||Use the "HAS-A" test to identify Composite Objects.||No. I never create a separate class to deal with groups of objects, just a separate class for each member of that group. It does not matter whether that grouping is called Object Composition or Object Aggregation, I do not put any special logic in a class to deal with such things. Refer to Using "HAS-A" to identify composite objects for details.|
|5||Favour composition over inheritance||No. Object composition is only used by those who misuse inheritance. The solution is to use inheritance properly, as explained in Use inheritance instead of object composition.|
|6||Create objects to deal with domain events.||No. A domain event is simply a user transaction which performs one or more operations on one or more database tables with the addition of any business rules. Objects in the Business/Domain layer are centered around entities (database tables) which have properties (data) and methods (operations). These objects represent nouns while the operations represent verbs. Objects in the Presentation or Data Access layers which perform standard operations on the data obtained from business objects are called Services. Domain objects have state while Services do not. Domain objects are large in number and are specific to their individual domains. Services are far smaller in number, are domain-agnostic, and are provided by the framework. Domain Events are therefore implemented as operations within the relevant domain object and not as separate service objects.|
|7||Create service objects to deal with operations which conceptually do not belong to any object.||No. All such objects have already been built and come supplied with the framework. These are Controllers, Views and Data Access Objects.|
|8||Use factories to create domain objects.||No. I don't need factories to configure any domain object as each object has only a single implementation, and that is handled with the class constructor.|
|9||Create a separate repository for each domain object.||No. I have a separate DAO for each RDBMS which can deal with any table with that RDBMS. I do not have a separate DAO for each table (domain object). Every table in the database is subject to the same operations - Create, Read, Update and Delete - which are specified using a query string, so a competent programmer should have no difficulty in building a single object which can handle those four operations on any database table.|
|10||Create a separate Controller for each Model.||No. Each user transaction performs one or more operations on one or more database tables, and as the operations which can be performed on any database table are exactly the same it is possible for a Controller to perform these operations on a table whose identity is not supplied until runtime. Each Model (table class) inherits the same set of public methods from a single abstract table class, so a Controller can be used with any Model which implements these methods - which in my case is ALL of them.|
The advantage of my heretical approach is that I am able to cut out a great deal of manual effort. All the domain-agnostic logic is provided by components in the framework, which means that I can spend the bulk of my time in dealing with that which has the most importance for the paying customer - the domain-specific logic. Amongst the things that I do not have to send time on are:
I have been building enterprise applications across numerous domains for several decades, and I have built a development framework for these applications in three different programming languages. This framework makes me very productive by providing huge volumes of domain-agnostic logic as standard from which I can then create user transactions into which I can, using my implementation of the Template Method Pattern, add domain-specific logic. I could never be as productive using an arse-backwards technique such as Domain Driven Design, so forgive me if I consign it to the toilet bowl.
Here endeth the lesson. Don't applaud, just throw money.
Here are some other heretical articles I have written on the topic of OOP:
Here are some articles on my framework:
|16 May 2021||Added The dark side of object aggregation|
|01 May 2021||Added Separate controller for each Use Cases|
|02 Mar 2019||Included references to The Template Method Pattern as a Framework
Added What is a "domain"?
|24 Jan 2019||Added The biggest differences with the most similarities|
|02 Dec 2018||Added a Comparison Matrix to summarise the differences between the "approved" approach and my "heretical" approach.|
|01 Oct 2018||Added Build a library of reusable components
Added Turning a library into a framework
Added Optimise the User Interface
Reworded the article to emphasize the fact that DDD starts with the domain-specific logic and adds in the domain-agnostic logic afterwards whereas my framework does the exact opposite. It is an implementation of the Template Method pattern which starts with domain-agnostic logic and allows domain-specific logic to be added in later via a pre-defined collection of "hook" methods.