Domain-driven design in Android — Part 1

The need for domain-driven design

We’ve come a long way since we wrote the entire logic for a feature inside a Fragment or Activity. But sometimes it’s hard to navigate the alternative approaches with their repositories, domain objects, DTOs, and use cases. What overarching purpose do they serve? Sometimes our understanding never surpasses the cursory, since sample projects are minimal and the primary sources are written from the point of view of enterprise web developers.

I’ve always wanted to return to the canon for repositories, domain objects and their like to ensure my understanding was correct. Reading Eric Evans’ Domain-Driven Design helped me place these in a cohesive framework. So the aim of this series of articles is to take this important text, Eric Evan’s Domain-Driven Design, and to paraphrase and adapt it for Android developers.

Putting the domain model to work

We write apps to deal with some activity of interest to the user. Perhaps this is the reasoning behind the name “Activity” for the primary class in Android. Whether you structure your app using Activities or Fragments, there will be a real “activity” related to your app — buying or selling goods, managing a Bluetooth peripheral and so-on.

When we write the app, we have to draw on knowledge related to this activity, which we will call a “domain.” For instance, we have to know which actions should be available to guests and which actions should be exclusively available to users that are signed in. While some of this work may be done by back-end developers on separate teams, we can’t escape with total ignorance of the sphere of activity.

It might not be possible for us to know this domain with as much nuance as an expert. Even if we work on an accounting app, devs can’t be expected to become experts accountants. In this case, we settle for a simplified model called a “domain model”:

A domain model is a rigorously organised and selective abstraction of the knowledge of an activity.

But why settle for this model when could leave the real-world activity in all its overwhelming detail and nuance, and we can translate information about the activity into code that suits our interest and ability as programmers? To this, Domain Driven Design gives three answers:

Why use a model?

1. The model and the heart of the design shape each other

Note that the above model is not the code itself. We implement the code based on the model, and the model is informed by the limitations of that which can be implemented in code This ensures we maintain a body of knowledge that is relevant, and code that is close to this body of knowledge.

2. The model is the backbone of a language used by all team members

Because the model and implementation approach each other, developers can use the language of the model to communicate both within the team, and to domain experts around the business.

3. The model is distilled knowledge

The model is a guide to the domain knowledge and captures its essence. When new information about the activity is discovered, or previous modelling has proved incomplete, developers and domain experts work together to incorporate a summary of this knowledge in the domain model.

The model is distilled knowledge. Photo by Jaap Mol on Unsplash

The need to model a domain

While Android developers love to show their technical flair in code, the primary challenge for engineers is to solve domain-related problems for the user with our app. When the domain is inherently complex, technical flair does not shield us from resultant complexity in the code. No amount of Kotlin syntactic sugar or fancy frameworks will save us here.

How many codebases have you seen fall down, overwhelmed by sheer complexity? One sign of defeat by complexity is having to translate a simple product requirement into an abstruse series of calls in the code. This is a sign of the absence of a model, or that implementation and model have deviated from each other. Either way, we have wandered far from the path of Domain-Driven Design.

Just as much as we value knowledge of kotlin-stdlib, Dagger, or lifecycle callbacks, we should also value modelling skills that enable a codebase where model and implementation are close. This makes for an Android developer who is tackling the complexity at the heart of software.

Wandering far from Domain-Driven Design. Photo by Paul Gilmore on Unsplash

Times of special importance

From my perspective, knowledge of a domain and how to model it become especially important in the following three scenarios for Android development:

  1. When the app talks to an external device. In this case, the Android device becomes almost like a back-end. The device will have its own protocol for connections and communication and this must be modeled correctly. The device may also have state and data that require correct modeling.
  2. When you are writing a library. A non-trivial library will provide abstractions that model a particular domain e.g., a Bluetooth connection, storage, or concurrency.
  3. When you are working closely with back-end developers or developing the back-end yourself. In this case, modelling the domain correctly becomes critical as changes involve coordination between multiple projects. Even if you don’t work with a back-end team and simply are a victim of their upstream changes, you may still need modelling skills to adapt the models you inherit into something more malleable.

Continued in part 2