Overview of
Domain-Driven Design

Tim McIver

DDD: What Is It?

Domain-Driven Design

  • Emphasizes the domain model as the central piece of an application
  • Emphasizes direct collaboration between developers and domain experts
  • Results in a "Ubiquitous Language" that is used to communicate domain concepts

Building Blocks

4 Basic Types

  • Entities
  • Value Objects
  • Repositories
  • Services

Entities

are domain objects whose identity is NOT defined by their attributes

  • Consider two people with the same name; they are not the same entity!
  • Some entities will have natural identifiers. E.g., SSN.
  • If there is no natural identifier, one must be generated for it at creation time.

Value Objects

are objects that ARE defined by their attributes

  • Need no separate identifier
  • Should be immutable

Some Notes About References

  • An Entity should reference other Entities by their identifiers - not through direct references.
  • A reference to an Entity should be retrieved by using a Repository.
  • References to Value Objects must be by direct reference since they have no identifier.

Example

// Entity
class User {
  private $id; // could have used SSN, if it was available
  private $name;
  private $address;

  public function changeName($newName) { $this->name = $newName; }
  public function changeAddress($newAddress) { $this->address = $newAddress; }
}

// Value Object
class Name {
  private $firstName;
  private $lastName;
}

// Value Object
class Address {
  private $street;
  private $city;
  private $state;
  private $zip
}

Repositories

  • are objects used to retrieve entities from and persist entities to storage.
  • Recommend creating an interface or trait so that different implementatinos can be provided (this is the Strategy Pattern: https://en.wikipedia.org/wiki/Strategy_pattern)

Example Repository

interface Repository {
  function getById($id);
  function save($entity);
}

class MySqlRepository implements Repository {

  function getById($id) { /* query real MySQL database */ }
  function save($entity) { /* insert data into real MySQL database */ }
}

class InMemoryRepository implements Repository {
  private $db; // array of id to entity

  function getById($id) { /* query $db */ }
  function save($entity) { /* mutate $db */ }
}

Services

are objects that

  • orchestrate coordination of complex domain logic
  • ideally will allow clients to perform this complex action with a single method call

Example Service

class OpenEnrollment(oeRepo: Repository[OpenEnrollment],
                     titanClient: TitanClient) {
  def edit(oeId: UUID, startDate: Instant, endDate: Instant): Future[Unit] = {
    for { // In Future
      // get the OE domain object
      oe <- oeRepo.getById(oeID)

      // edit the OE
      editedOe <- editOpenEnrollent(oe, startDate, endDate)

      // save it
      _ <- oeRepo.save(editedOe)

      // conditionally "run" it
      _ <- if (editedOe.status === OpenEnrollmentState.Ready)
             runOpenEnrollment(editedOe)
           else
             Future.successful(())
    } yield ()
  }

  def runOpenEnrollment(oe: OpenEnrollment): Future[Unit] = { ... }
}

Bounded Contexts

  • Could be defined as "a boundary within which a given model applies".
  • Similarly named models in different bounded contexts could be very different
    • One could be an Entity while the other a Value Object.
  • Typically a bounded context is implemented as its own microservice.

References

Questions?