DDD Immersion

June Sold out in New York & London.
Book early for August.

Course descriptions, registration and more dates here  »
Or call us at: (+1) (914) 861-5264

Getting Started with DDD When Surrounded by Legacy Systems

Strategy 1: Bubble Context

(Part 1 of a series by Eric Evans)
  1. Why we need the bubble
  2. How we form the bubble
  3. What becomes of the bubble

Why we need the bubble

We say that effectively applying the tactical techniques of domain-driven design (DDD) requires a clean, bounded context (See September 2010 newsletter). This can be a daunting requirement when your work is dominated by legacy systems. These systems are often tangled, and even when they are orderly they are usually not suited to DDD. In a series of articles over the next few months, I'll describe strategies for getting started with DDD when you have a big commitment to legacy systems.

All of these strategies will be ways of establishing a new bounded context. Attempts to employ the DDD tactics in the context of a legacy system almost always disappoint. One of the fundamentals of DDD is that we choose a model (by which we mean a system of abstractions) well suited to the problem at hand, yet a legacy system already has an established model, albeit implicit, and this model can seldom be changed with a reasonable amount of effort. Even if the legacy model could be changed, the new model might not suit the legacy functionality -- the change could undermine what the legacy system was always good at. On the other hand, simply adding objects that express a distinct model without changing the ones already there will lead to conflicting rules and concepts.

Then again, sometimes the old model is respected, the building blocks being intended as a way to bring order and expressiveness, rather than model transformation. Although in theory this could work, in practice it seldom succeeds because most such systems have multiple, intermingled models and/or the teams have disorderly development habits. These factors disrupt subtle design elements (especially in the case of a 'Big Ball of Mud').

This leads some organizations to introduce DDD with legacy replacement projects and other ambitious initiatives. I advise against this also. Such efforts are high-risk even for a team that already has mastered whatever techniques are to be employed. As I've pointed out many times, legacy replacement is usually a bad strategy, only made worse by simultaneously introducing dramatic changes in process. Introducing a difficult new set of development principles and techniques is best done incrementally, as in a pilot project, in a way that allows members of the team to gain experience and allows the organization to assess the approach. The "bubble context" is a good candidate in this situation.

This first strategy does not require a big commitment to DDD. It allows even a small team to achieve a modest objective involving some intricate domain logic and, ideally, one with some strategic value. Then at some point the bubble bursts. The carefully designed code is gradually reabsorbed by the legacy. It is no longer a platform for innovative new development.

Yet the new functionality does not disappear. It will continue to be maintained as an extention of the legacy systems. The organization now has the experience to undertake more ambitious use of DDD.

Main Characteristics of the Bubble Context Strategy

  • Modest commitment to DDD
  • No synchronization risk (uses legacy database)
  • Works when there is a limited range of data needed from legacy

How we form the bubble

A 'bubble' is a small bounded context established using an Anticorruption Layer (ACL) for the purpose of a particular development effort and not intended necessarily to be expanded or used for a long time.

To get started, you need to choose an important, yet modest-sized, business related problem with some intricacy. You'll need a small, self-disciplined team (cherry-picked, if necessary) that has control over its code. The bubble isolates that work so the team can evolve a model that addresses the chosen area, relatively unconstrained by the concepts of the legacy systems.

Anticorruption Layer and Translation

The context boundary of the bubble is established with the popular ACL (see Chapter 14. Summaries here.).

Great Wall of China: A different sort of ACL
Image of Great Wall of China

This boundary isolates your new work from the larger system, allowing you to have a very different model in your context than exists just on the other side of the border.

For an example, imagine our organization has two existing legacy systems, each of which tracks some customers' credit cards and checking accounts. Now we want to implement a feature called 'Can I buy it?', which figures out how much the customer could theoretically lay out for an impulse buy. This would be their balance in checking plus their credit card limit minus the charges (or 'drawdowns') on their cards. (Note that this feature is actually too simple to justify a bubble context in a real project. Examples have to be simple.)

Desired new model
Class Diagram of New Model

Each of the legacy contexts defines some concepts which are mapped to the abstractions of our bubble context. First, a subsystem which we'll call "Context A" works according to this class diagram:

Existing model in Context A
Class Diagram of Model in Legacy Context A
    These objects can be mapped to our new model:
  • CheckingAccount.available balance → Funds.amount
  • CreditAccount.available credit → Funds.amount

Then "Context B" works according to this class diagram:

Existing model in Context B
Class Diagram of Model in Legacy Context B
    These objects can also be mapped to our new model:
  • CheckingAccount.balance → Funds.amount
  • CreditCard.limit → Funds.amount
  • CreditCard.balance → Drawdown.amount

Translation itself can be tricky. There are alternative ways to populate the new objects from the legacy data, and some of them might work as well as these. Others would be undesirable, even though they might produce the right numbers.

    For example, in the following translation of Context A, the semantics don't match, even though the calculated AvailableFunds.amount would still be correct:
  • CheckingAccount.balance → Funds.amount
  • CheckingAccount.holds → Drawdown.amount
Both holds and drawdowns reduce the available, but that doesn't make them the same thing. This would make for a brittle design and poor communications with business stakeholders.

    To illustrate another pitfall, the following translation of Context B would be semantically consistent, yet it would move business logic into the translator:
  • CreditCard.limit - CreditCard.balance → Funds.amount
The relationship between funds, limit and balance should be modeled in a bounded context. There are gray areas, but ideally the translator should just translate.

So working out the translation is an important part of the analysis when using an anticorruption layer. The implementation of that translator can take many forms. As much as practical, it should be kept loosely coupled to the rest of the ACL.

Now I'll turn to the interface of the ACL.

ACL-backed Repository

A nice technique that is sometimes used to set up a bubble context is the 'ACL-backed Repository'.

Remember, when we set up a bubble context, our data is coming from one or more legacy systems. We don't create a new database within the new context. We query the legacy database and rearrange the data into the new concepts in the ACL.

The DDD Building Block used to represent access to pre-existing objects is the 'Repository' (see Chapter 6. Summaries here.). We tend to think of a repository as a thin layer over a data access layer, which is mapping some kind of data store into our objects.

Classic Repository implementation
Diagram of Classic Repository with Access to Data Store

Yet, as with any design abstraction, the implementation of a repository can be entirely different from the impression given by the interface. We can create the illusion that the bubble context has its own data store by implementing a repository interface using the ACL. Any time an object is needed within the bubble, it is requested from the repository. The repository implementation in the ACL coordinates the steps needed to answer. First it invokes legacy functions and/or queries the legacy database(s). It passes the returned data to the translators, which rearrange the information into the objects used in the bubble. Finally, the ACL returns those objects through the repository interface. The repository's contract is fulfilled without having any data of its own.

ACL-Backed Repository implementation
Diagram of Repository Backed by Anticorruption Layer

It should be clear at this point that the anticorruption layer is a significant piece of software in its own right. It should be explicitly addressed in budgeting and planning. It should be designed explicitly as other components are, with particular care to keep it decoupled from the actual business logic of the new system. A good anticorruption layer has a clean interface entirely in terms of the downstream model (in our case the bubble model) that provides access to the information and services of the upstream system.

This means that development in the downstream context can focus on the business problem and not be distracted or complicated by dealing with the distinct concepts, and the quirks, of the other system. On the other hand, it also means that any refactoring of model elements where the information comes from the legacy context is extra work. We must refactor the new system and also the ACL. And any new information needed from the legacy context requires that we incorporate the new information into the bubble model, work out the mapping from the old model, and then enhance of the ACL. A high price. So we should choose functionality valuable enough to justify it.

What becomes of the bubble

Development in the bubble could go on for several months, or, occasionally, in cases where the interface with the other context is relatively narrow, for a few years. However, it will not last forever.

Sometimes, when the project has ended in success, people decide to make the new context the basis for more ambitious new development. When this happens, the limitations of the bubble context may be judged unacceptable, and the strategy migrates to the Synchronizing ACL strategy (which will be the subject of an upcoming newsletter article). Then the bubble transforms something more long-lasting.

More often, the bubble is temporary. Perhaps, as more is done in the new context, the discipline of the early work is not maintained. Holes appear in the ACL. The bubble bursts. Sometimes, with the initial work completed, interest dissipates. Neglect eventually leads to loss of the subtle design elements and bypassing of the ACL, and the bubble's isolation is gone.

Although most bubbles are short-lived, that does not mean they don't provide value. They allow a team and an organization to gain experience with DDD with a minimal commitment and low risk. They allow an intricate problem to be solved which would have been difficult to solve in the legacy context. And although the dissolution of the bubble means that tactical DDD will not be very helpful in changing or extending what was done, the functionality that was built in the bubble will live on long after the bubble is gone. As the boundary becomes less distinct, the fossilized model expression becomes just one of many in the mosaic of the legacy system.

The bubble context is a great way to get started with DDD while coexisting with legacy systems. In the coming months, I'll write about other strategies of varying levels of ambitiousness.

In This Issue

Breakfast Seminar
New York

Learn what Domain-Driven Design (DDD) is and what its benefits are over a continental breakfast. Eric Evans, the author of the book Domain-Driven Design, will be hosting this 2-hour seminar which includes a presentation and informal discussion. This introduction is non-technical and assumes no prior knowledge of DDD.

Time: June 20, 9:00-11:00
Place: One New York Plaza, New York, NY
Price: $50

Event has passed.
No registration available.

Upcoming Classes

June 6, London, UK Sold Out
June 14, Denver, CO   3 Left
June 14, New York   Sold Out
July 5, London, UK
Aug 23, New York, NY
Sept 20, Denver, CO
Sept 27, London, UK
Nov 29, New York, NY

Course descriptions, registration and more dates here  »

London in June: 3rd Annual DDD Exchange

DDD Exchange 2011, a one-day conference dedicated to DDD.

Udi Dahan, Eric Evans, and Greg Young at Park Bench Discussion

Some Highlights:

  • Jim Webber on REST and DDD
  • Udi Dahan on CQRS
  • Eric Evans
  • Greg Young
DDD Exchange is a truly interactive session where we can delve into the tricky questions that leaders in DDD are struggling with at this moment.

More details, or book online »

Newsletter Archive Online

You can browse all past issues in our Newletter Archive  »