Friday, June 24, 2011

An introduction to Domain-Driven Design

This post tries to capture the need of Domain-Driven Design. It’s far from complete in terms of detailing all the best practices that can be applied in this field. The goal of the article is to introduce the reader to the terminology and the value of doing Domain-Driven Design and the dangers of not doing it. An example Domain-Driven approach to Legacy Modernization is used to cement the idea.

 

ReferencesDomain-Driven Design Community

 

Software Complexity

The approach to design mainly determines how complex a piece of software can become. Software being too rigid to support changes or higher defect solving times often are indicators of high complexity. Software complexity can be broadly categorized into two types: Technological Complexity and Domain Complexity.

Technological Complexity

Technical constraints induce this type of complexity. Design considerations are made to tackle such constraints. A great amount of effort has gone into the design of networks, databases and other technological areas. By now many books have been written on these topics and developers have cultivated their skills to overcome these factors.

Domain Complexity

Domain complexity is induced by the domain where the software resides. A piece of software confused with domain terms, conditions and processes are often indicators of this form of complexity.

 

Why Domain-Driven Design?

Advances in the area of Technological Complexity have made it possible to reduce it's effect. Further, this complexity is often easily recognizable. For example, a design chosen to solve database locking can be documented as a technical design decision with reference implementation (which appears in the source code). Also, such complexity affects a particular layer in the software architecture, which makes it easier to tackle and maintain.
Domain Complexity, on the other hand, is a lot more complicated. Domain complexity generally affects all or most layers of the software architecture. If this complexity is not solved in the design, it wouldn't matter if all of the other technological complexities are dealt with. This domain complexity will, on its own, result into software which would not be maintainable and one which won't respond to changes. By incorporating the domain while designing software helps overcome this complexity. Domain-Driven Design tries to address this complexity.

 

What is Domain-Driven Design?

Domain-Driven Design, as the name suggests, is developing software keeping the business domain in the central position. It's not just about having a design in place before implementing the software. I assume that this is always the case. But the focus on the business domain is not so natural in the process of software development. Domain-Driven Design stresses on having a model for complex domain designs to lay focus on domain and domain logic. While designing such a model, the definition of ‘Bounded Context’ required careful attention.

Bounded Context

A model of a domain consists of abstractions and relationships between them. If you agree with the fact that there are always multiple models, you would also agree that there are multiple contexts. What is a context? A context is necessarily a boundary within which a particular abstraction or definition holds true. For example, a Loan in a Mortgages context is always coupled with an Insurance; which may not be true in another context. It's crucial to define this context and then begin defining abstractions within them. Key rules which hold true within such a context:
  1. There is no duplication within a context. An abstraction conveys the same meaning throughout the whole context.
  2. There is ONE unified model within a context.
  3. Duplication is allowed between two contexts.

 

Example: A Domain-Driven Approach to Legacy Modernization

Most businesses feel the pain and therefore the need to get rid of legacy applications. Various approaches to Legacy Modernization are conceivable (at least in theory). I'll discuss couple of them:

Approach 1: Shutting down legacy

One of the approaches is to shutdown the existing legacy bits and kick off green-field development projects which would re-build the existing applications in a cool, modern architecture. An enthusiastic team then attempts to capture requirements of the new system. This is done either by extracting business logic from existing systems (called Harvesting) or talking to the business all over again. Either which ways, it's a tedious step. The second step of Forward Engineering typically consists of laying out the new architecture, getting infrastructure to support it and then employing developers and testers to implement the requirements.
Problems with this approach:
Chance of the same mistakes done again - This approach focuses on the core domain right at the end (a stage you may never end up in). The whole step of gathering/extracting requirements and setting up the infrastructure take away so much time that the project(s) is already delayed and one ends up with intensified development (sacrificing key principles, best practices) and hardly any testing. If one is lucky, this results in a new system which basically contains the same mistakes of the existing, legacy system, but now in a new language / platform. Building legacy all over again! I said lucky in the last sentence since this is really an extremely hopeful case which a project may not reach (it might be stopped much before that).
Questions to be asked:
  • Why would someone (in this case The Business) want to build a new system exactly same as the existing one?
  • What are the real pains with the existing ones? Maintainence time? Resistance to changes? Awkward connections? Are these pains really technological or due to lack of domain design?

Approach 2: A Domain-driven approach

Basic Principles:
  • Treat legacy as Assets
  • Focus on the core domain
Gradually phase-out the existing legacy systems and work on the core domain. Define the various contexts the existing systems cater to. Define unified models within that domain. Build an anti-corruption layer(s) which handles translation between the existing legacy systems and the core domain. These layers will be very very ugly but keep the core beautiful. Slowly move over to the services you wish the new system to provide. These reside on top of the core domain. Expand the system until all of the functionality catered to or expected from the legacy systems are covered.
Positives:
  1. Due to a step-by-step take over approach, there will always be something live. This gets a business buy-in of the project.
  2. The new system will not be a copy of the existing system; rather it would be the modern system which one hoped for.