DDD - Modelling the Domain - Repositories

The role of a Repository is to abstract away the database infrastructure by retrieving and persisting aggregates, their entities and value objects, giving the illusion of an in-memory data store (Evans, 2003). Typically, for each domain object that has a unique identity and needs persistence, we need to define a Repository in the Domain Model (Evans, 2003).

This repository is not a concrete implementation but rather an interface or an abstract class (Vernon, 2013) as shown in the code below:

from abc import ABCMeta

class ArticleRepository(metaclass=ABCMeta):

	@abstractmethod
	def next_identity(self):
		"""Returns next unique identifier"""

	@abstractmethod
	def get(self, article_id):
		"""Get an instance of an Article using its identifier"""

	@abstractmethod
	def find_by_title(self, keyword):
		"""Finds all articles where keyword matches its title""" 

	@abstractmethod
	def find_by_tag(self, tag):
		"""Finds all articles matching the specified tag"""

	@abstractmethod
	def add(self, article):
		"""Adds the article to the repository"""

	@abstractmethod
	def remove(self, article):
		"""Removes the article from the repository"""

In the early stages of implementing a software system we could implement the above repository using an in memory data store e.g. using a simple dictionary as shown below:

class InMemoryArticleRepository(ArticleRepository):

	def __init__(self):
		self._articles = {}  # an empty dictionary

	def next_identity(self):
		return str(uuid.uuid4())

	def get(self, article_id):
		return self._articles[article_id]

	def find_by_title(self, keyword):
		found = []
		for article in self._articles.values():
			if keyword in article.title:
				found.append(article)
		return found

	def find_by_tag(self, tag):
		found = []
		for article in self._articles.values():
			if tag in article.tags:
				found.append(article)
		return found

	def add(self, article):
		self._articles[article.id] = article

	def remove(self, article):
		self._articles.pop(article.id)

The implementation above provides fetching and saving articles into memory. Thus, any decisions on the database technology can be postponed for later and it helps us focus on the Domain Model without being distracted with ERD diagrams and the impedance mismatch between the domain objects and the relational schema (Nilsson, 2006). In addition, when writing unit tests we do not depend on the existence of a running database instance in order to retrieve an article and test its behaviour (Evans, 2003).

Vernon (2013) categorised the above example as a collection oriented repository i.e. it mimics a simple list of objects hiding completely any indication of a persistence data store. However, a concrete implementation depends on the ability of the database access framework to detect any domain object changes and persist them automatically (Vernon, 2013). If the chosen framework does not track object changes then we could implement the Unit of Work (UoW) design pattern ourselves, as described by Fowler (2002), although properly implementing UoW is a significant exercise. To overcome this limitation, Vernon (2013) identified the persistence oriented repository which is essential a collection oriented repository with the addition of a save method, that either inserts or updates records.

Although, we do not expect Domain Experts to describe their business in terms of Repositories and Factories, their role is very important in designing a complex software system (Evans, 2003).

References

  • Evans, E. (2003) Domain-Driven Design: Tacking Complexity In the Heart of Software. Boston: Addison-Wesley.
  • Fowler, M. (2002) Patterns of Enterprise Application Architecture. Boston: Addison-Wesley.
  • Nilsson, J. (2006) Applying Domain-Driven Design and Patterns. Boston: Addison-Wesley.
  • Vernon, V. (2013) Implementing Domain-Driven Design. Boston: Addison-Wesley.

This post is an excerpt of my MSc Thesis titled "Applying Domain-Driven Design in Python - designing a self-hosted Read-it-Later service" (January 2014).

This article was updated on

Related post

DDD - Modelling the Domain - Services

When designing a Domain Model the prominent modelling paradigm is objects and as a result we are mapping concepts i.e. nouns to DDD building blocks such as Entities and Value Objects. The behaviour associated to those concepts is mapped to methods using verbs. However, there are domain specific functions that we would like to define in the model but cannot be attached into an existing Entity or Value Object (Evans, 2003). Instead of forcing foreign operations into domain objects, Evans (2003) introduced the concept of Domain Services.

DDD - Modelling the Domain - Value Objects

There are elements of the Domain Model without any conceptual identity which are typically used to characterise Entities (Evans, 2003). Those elements are called Value Objects and their significance is often neglected (Vernon, 2013). Examples of Value Objects are: Zip Code, Order Number, Phone Number, Money, Currency, Date, Date Range, E-mail Address, URL etc. Value Objects express domain concepts and are part of the Ubiquitous Language (Evans 2003; Vernon, 2013).