Presentation Domain Data Layering

26 August 2015

Martin Fowler

team organization

encapsulation

application architecture

web development

One of the most common ways to modularize an information-rich program is to separate it into three broad layers: presentation (UI), domain logic (aka business logic), and data access. So you often see web applications divided into a web layer that knows about handling HTTP requests and rendering HTML, a business logic layer that contains validations and calculations, and a data access layer that sorts out how to manage persistent data in a database or remote services.

On the whole I've found this to be an effective form of modularization for many applications and one that I regularly use and encourage. It's biggest advantage (for me) is that it allows me to reduce the scope of my attention by allowing me to think about the three topics relatively independently. When I'm working on domain logic code I can mostly ignore the UI and treat any interaction with data sources as an abstract set of functions that give me the data I need and update it as I wish. When I'm working on the data access layer I focus on the details of wrangling the data into the form required by my interface. When I'm working on the presentation I can focus on the UI behavior, treating any data to display or update as magically appearing by function calls. By separating these elements I narrow the scope of my thinking in each piece, which makes it easier for me to follow what I need to do.

This narrowing of scope doesn't imply any sequence to programming them - I usually find I need to iterate between the layers. I might build the data and domain layers off my initial understanding of the UX, but when refining the UX I need to change the domain which necessitates a change to the data layer. But even with that kind of cross-layer iteration, I find it easier to focus on one layer at a time as I make changes. It's similar to the switching of thinking modes you get with refactoring's two hats .

Another reason to modularize is to allow me to substitute different implementations of modules. This separation allows me to build multiple presentations on top of the same domain logic without duplicating it. Multiple presentations could be separate pages in a web app, having a web app plus mobile native apps, an API for scripting purposes, or even an old fashioned command line interface. Modularizing the data source allows me to cope gracefully with a change in database technology, or to support services for persistence that may change with little notice. However I have to mention that while I often hear about data access substitution being a driver for separating the data source layer, I rarely hear of someone actually doing it.

Modularity also supports testability, which naturally appeals to me as a big fan of SelfTestingCode . Module boundaries expose seams that are good affordance for testing . UI code is often tricky to test, so it's good to get as much logic as you can into a domain layer which is easily tested without having to do gymnastics to access the program through a UI 1 . Data access is often slow and awkward, so using TestDoubles around the data layer often makes domain logic testing much easier and responsive.

1: A PageObject is also an important tool to help testing around UIs.

While substitutability and testability are certainly benefits of this layering, I must stress that even without either of these reasons I would still divide into layers like this. The reduced scope of attention reason is sufficient on its own.

When talking about this we can either look at it as one pattern (presentation-domain-data) or split it into two patterns (presentation-domain, and domain-data). Both points of view are useful - I think of presentation-domain-data as a composite of presentation-domain and domain-data.

I consider these layers to be a form of module, which is a generic word I use for how we clump our software into relatively independent pieces. Exactly how this corresponds to code depends on the programming environment we're in. Usually the lowest level is some form of subroutine or function. An object-oriented language will have a notion of class that collects functions and data structure. Most languages have some form of higher level called packages or namespaces, which often can be formed into a hierarchy. Modules may correspond to separately deployable units: libraries, or services, but they don't have to.

Layering can occur at any of these levels. A small program may just put separate functions for the layers into different files. A larger system may have layers corresponding to namespaces with many classes in each.

I've mentioned three layers here, but it's common to see architectures with more than three layers. A common variation is to put a service layer between the domain and presentation, or to split the presentation layer into separate layers with something like Presentation Model . I don't find that more layers breaks the essential pattern, since the core separations still remain.

The dependencies generally run from top to bottom through the layer stack: presentation depends on the domain, which then depends on the data source. A common variation is to arrange things so that the domain does not depend on its data sources by introducing a mapper between the domain and data source layers. This approach is often referred to as a Hexagonal Architecture .

These layers are logical layers not physical tiers. I can run all three layers on my laptop, I can run the presentation and domain model in a desktop with a database on a server, I can split the presentation with a rich client in the browser and a Backed For Frontend on the server. In that case I treat the BFF as a presentation layer as it's focused on supporting a particular presentation option.

Although presentation-domain-data separation is a common approach, it should only be applied at a relatively small granularity. As an application grows, each layer can get sufficiently complex on its own that you need to modularize further. When this happens it's usually not best to use presentation-domain-data as the higher level of modules. Often frameworks encourage you to have something like view-model-data as the top level namespaces; that's OK for smaller systems, but once any of these layers gets too big you should split your top level into domain oriented modules which are internally layered.

Developers don't have to be full-stack but teams should be.

One common way I've seen this layering lead organizations astray is the AntiPattern of separating development teams by these layers. This looks appealing because front-end and back-end development require different frameworks (or even languages) making it easy for developers to specialize in one or the other. Putting those people with common skills together supports skill sharing and allows the organization to treat the team as a provider of a single, well-delineated type of work. In the same way, putting all the database specialists together fits in with the common centralization of databases and schemas. But the rich interplay between these layers necessitates frequent swapping between them. This isn't too hard when you have specialists in the same team who can casually collaborate, but team boundaries add considerable friction, as well as reducing an individual's motivation to develop the important cross-layer understanding of a system. Worse, separating the layers into teams adds distance between developers and users. Developers don't have to be full-stack (although that is laudable) but teams should be.

Further Reading

I've written about this separation from a number of different angles elsewhere. This layering drives the structure of P of EAA and chapter 1 of that book talks more about this layering. I didn't make this layering a pattern in its own right in that book but have toyed with that territory with Separated Presentation and PresentationDomainSeparation .

For more on why presentation-domain-data shouldn't be the highest level modules in a larger system, take a look at the writing and speaking of Simon Brown . I also agree with him that software architecture should be embedded in code.

I had a fascinating discussion with my colleague Badri Janakiraman about the nature of hexagonal architectures. The context was mostly around applications using Ruby on Rails, but much of the thinking applies to other cases when you may be considering this approach.

Acknowledgements

presentation domain data

Clean Architecture Guide (with tested examples): Data Flow != Dependency Rule

Mario Sanoguera de Lorenzo

Mario Sanoguera de Lorenzo

ProAndroidDev

Hello everyone! 👋 In this story I want to explain Clean Architecture (with tested examples) & talk about the most common mistake when adopting it (the difference between Data Flow and Dependency Rule).

This story is the follow up after my latest medium Intro to App Architecture. Make sure to check it out if you are interested in what makes a good Architecture & why + how to achieve a more maintainable codebase.

Let’s start explaining Data Flow in Clean Architecture with an example.

Imagine opening an app that loads a list of posts which contains additional user information. The Data Flow would be:

1. UI calls method from Presenter/ViewModel. 2. Presenter/ViewModel executes Use case. 3. Use case combines data from User and Post Repositories. 4. Each Repository returns data from a Data Source (Cached or Remote). 5. Information flows back to the UI where we display the list of posts.

From the example above we can see how the user action flows from the UI all the way up to the Data Source and then flows back down. This Data Flow is not the same flow as the Dependency Rule.

Dependency Rule

Dependency Rule is the relationship that exists between the different layers. Before explaining the Dependency Rule in Clean Architecture lets rotate the onion 90 degrees. This helps to point out layers & boundaries. 🆒

Let’s identify the different layers & boundaries.

Presentation Layer contains UI (Activities & Fragments) that are coordinated by Presenters/ViewModels which execute 1 or multiple Use cases. Presentation Layer depends on Domain Layer.

Domain Layer is the most INNER part of the onion (no dependencies with other layers) and it contains Entities, Use cases & Repository Interfaces. Use cases combine data from 1 or multiple Repository Interfaces.

Data Layer contains Repository Implementations and 1 or multiple Data Sources. Repositories are responsible to coordinate data from the different Data Sources. Data Layer depends on Domain Layer.

Explaining the Domain Layer

Domain (with business rules) is the most important Layer.

Domain is at the center of the onion which means it is the core of our program. This is one of the main reasons why it shouldn’t have any dependencies with other layers.

Presentation and Data Layers are less important since they are only implementations that can be easily replaced. The list of posts could be displayed in Android, iOS, Web or even Terminal if your code is properly decoupled. The same happens with a Database or any kind of Data Source, it can be easily switched.

The outer you go on the onion the most likely things are prone to change. One of the most common mistakes is to have your app driven by your data layer/specific data system. Making it hard to replace or bridge with different data sources down the line.

Domain Layer does NOT depend on Data Layer.

Having modules with the correct dependency rules means that our Domain doesn’t have any dependency on any other layer. Due to no dependencies to any Android Library the Domain Layer should be a Kotlin Module. This is an extra boundary that will prevent polluting our most valuable layer with framework related classes. It also promotes reusability across platforms in case we switch over the Framework as our Domain Layer is completely agnostic.

Explaining the Domain Layer with an example!

What is the real problem that we need to solve?

“Load a list of posts with some user information for each post”

This is the core of our solution no matter where the data comes from or how we present it. This belongs to a Use case inside our Domain Layer which is the most inner layer of the architecture (business logic).

For those who haven’t tried Clean Architecture yet Use cases will avoid God Presenters/ViewModels since the Presentation Layer will only execute Use cases and notify the view (Separation of concerns + Single Responsibility Principle). This will also improve the RUDT points ( R ead, U pdate, D ebug & T est) of your project.

Tests here (omitted to make the story shorter).

This Use case is combining data from 2 repositories (UserRepository & PostRepository).

How does Domain NOT depend on Data?

This is because Use cases in Domain are not using the actual implementation of the Repository that sits in the Data Layer. Instead, it is just using an abstraction/interface in the Domain Layer that acts as a contract for any repository who wants to provide the data.

In Clean Architecture it is the responsibility of the Data Layer to have 1 or multiple implementations of the Domain’s interfaces and to bind the interface with the actual implementation.

Dagger2 binding example:

Koin binding example:

This abstraction with the interface and its binding is the Dependency Inversion principle (D from SOLID) which is a way of decoupling modules.

High-level modules should not depend on low-level modules, both should depend on abstractions.

In simple terms, this means adding/depending on interfaces so we can easily switch the implementation and decouple our software modules.

Explaining the Data Layer: Repositories Implementation & Data Sources

The Repository Implementation implements the Domain Repository Interface and is in charge of combining 1 or multiple Data Sources. The most common ones are Memory, Cache & Remote.

This Repository is pulling from the cache and remote interfaces (each Data Source then binds to an implementation). This decoupling makes the Repository agnostic from its Data Sources, avoiding changes when switching a Data Source implementation.

Repositories expect from Data Sources the Domain Models already so it pushes the responsibility of mapping from Data to Domain to each individual Data Source implementation (or from Domain to Data in case you are pushing something to the Data Source).

Data Strategies.

Multiple Data Sources lead to different Data Strategies. My favorite is to only return Cache (unique source of truth) and to refresh Cache from Remote only when it is empty or there is a user action (swipe to refresh for ex.). This saves lots of data and was inspired after reading Build for the next billion users from Android Developers.

Final recap:

I’ve omitted the Presentation Layer as it only needs to execute use cases & display data. Remember that each layer has its own entities & mappers and that in order to keep our Domain Layer with no dependencies Data and Presentation are responsible to map to/from Domain Entities depending if you are pushing or pulling data.

Done! 👏 👏 👏

Sanogueralorenzo/android-kotlin-clean-architecture, android sample clean architecture app written in kotlin - sanogueralorenzo/android-kotlin-clean-architecture.

You can check my sample app that was used as an ex. by clicking up here ☝️

→ My friend Igor Wojda 👋🤖 ’s Clean Architecture slides and talk .

→ Issue asking why Domain isn’t depending on the Data layer.

Remember to follow, share & hit the 👏 button if you’ve liked it! (:

GitHub | LinkedIn | Twitter

Mario Sanoguera de Lorenzo

Written by Mario Sanoguera de Lorenzo

Staff Engineer @Tonal

More from Mario Sanoguera de Lorenzo and ProAndroidDev

Intro to App Modularization

Intro to App Modularization

Hello everyone 👋 in this story i want to give a brief introduction to app modularization, some thoughts around it & first steps you can….

Protect your code from Gemini in Android Studio

Katie Barnett

Protect your code from Gemini in Android Studio

The studio bot is great, but be sure you are not sharing proprietary code unintentionally.

Kotlin Design Patterns: Mediator

Michal Ankiersztajn

Kotlin Design Patterns: Mediator

Mediator is a behavioral design pattern that reduces object coupling and dependencies chaos..

Detecting Kotlin Code Smells with Detekt

Detecting Kotlin Code Smells with Detekt

Hello everyone 👋 in this story i will show how to improve your kotlin codebase with detekt, a kotlin static analysis tool., recommended from medium.

Multi-Module with Clean Architecture

Syed Ovais Akhtar

Multi-Module with Clean Architecture

A multi-module app, in the context of software development, refers to an application that is structured and organized into multiple….

A flexible, modern Android app architecture: complete step-by-step

A flexible, modern Android app architecture: complete step-by-step

Here we teach android architecture by example. that means showing *how* various architecture decisions are made. we will encounter….

presentation domain data

Medium's Huge List of Publications Accepting Submissions

presentation domain data

Staff Picks

Jetpack Compose : MVI Architecture with Retrofit2, Dagger-Hilt (Full Guide)

Jetpack Compose : MVI Architecture with Retrofit2, Dagger-Hilt (Full Guide)

Hello composers 👋🏻, in this blog, we’ll learn what is app architecture and why it is important while developing apps. which is the….

Android Development: Clean Architecture with MVVM and SOLID Principles

Basanta Behera

Android Development: Clean Architecture with MVVM and SOLID Principles

# android development: clean architecture with mvvm and solid principles.

Clean Architecture in Flutter | MVVM | BloC | Dio

Yamen Abdulrahman

Clean Architecture in Flutter | MVVM | BloC | Dio

Clean architecture is a software design paradigm introduced by robert c. martin, and it aims to create maintainable and scalable software….

UseCase Red Flags and Best Practices in Clean Architecture

Teknasyon Engineering

UseCase Red Flags and Best Practices in Clean Architecture

Detailed guidelines about use cases..

Text to speech

404 Not found

logo

Visualizing Fowler’s Layering Principles

presentation domain data

In 2005  Martin Fowler  attended a workshop on enterprise software development where he and the other workshop attendees came up with and voted on a set of application layering principles. After the workshop, he wrote an article on his website that listed each of the principles and the number of up votes and down votes each principle received.

When I read this article, I thought it provided an interesting insight into the positive and negative sentiment that developers felt toward each application layering principle. However, I really wanted to see the data presented in a way that made it easier to digest visually. So I took the liberty of creating a tabular visualization of Fowler’s data ranked in order of the positive sentiment towards each principle (i.e. up votes minus down votes). In addition, I color coded each principle to indicate positive, neutral, or negative sentiment.

Source:  http://martinfowler.com/bliki/LayeringPrinciples.html

Fowler cautions against reading too much into this list, for various reasons; however, I do find the list interesting because it seems to match, and reinforce, several intuitions I have about application layering principles. In addition, I think this way of presenting the data might be valuable to others as well, so I am providing it here, with credit to Fowler, as requested on his FAQ:  http://martinfowler.com/faq.html

  • Cookies Policy

© 2024 Matthew Renze

Flutter App Architecture: The Domain Model

Andrea Bizzotto

Andrea Bizzotto

Feb 9, 2022 9 min read

Have you ever mixed up your UI , business logic and networking code into one messy bundle of spaghetti code?

I know I did. ✋

After all, real-world app development is hard.

Books such as Domain-Driven Design (DDD) have been written to help us develop complex software projects.

And at the heart of DDD lies the model , which captures important knowledge and concepts that are needed to solve the problem at hand. And having a good domain model can make all the difference between the success or failure of a software project.

Models are very important but they cannot live in isolation. Even the simplest of apps requires some UI (what the user sees and interacts with) and needs to communicate with external APIs to show some meaningful information.

Flutter Layered Architecture

In this context, it's often valuable to adopt a layered architecture, introducing a clear separation of concerns between different parts of the system. And this makes our code easier to read , maintain and test .

Broadly speaking, four different layers are normally identified:

  • presentation layer
  • application layer
  • domain layer

The data layer sits at the bottom and contains the repositories that are used to talk to external data sources.

Just above it, we find the domain and application layers. These layers are very important as they hold all the models and business logic of our app. For more info, read the other articles in this series:

  • Flutter App Architecture with Riverpod: An Introduction
  • Flutter Project Structure: Feature-first or Layer-first?
  • Flutter App Architecture: The Repository Pattern
  • Flutter App Architecture: The Presentation Layer
  • Flutter App Architecture: The Application Layer

In this article, we'll focus on the domain layer , using an eCommerce application as a practical example. As part of this, we will learn:

  • what is a domain model
  • how to define entities and represent them as data classes in Dart
  • how to add business logic to our model classes
  • how to write unit tests for that business logic

Ready? Let's go!

What is a Domain Model?

Wikipedia defines the domain model like this:

The domain model is a conceptual model of the domain that incorporates both behavior and data .

The data can be represented by a set of entities along with their relationships , while the behavior is encoded by some business logic for manipulating those entities.

Using an eCommerce application as an example, we could identify the following entities:

  • User : ID and email
  • Product : ID, image URL, title, price, available quantity etc.
  • Item : Product ID and quantity
  • Cart : List of items, total
  • Order : List of items, price paid, status, paymente details etc.
When practicing DDD, entities and relationships are not something we produce out of thin air, but rather the end result of a (sometimes long) knowledge discovery process. As part of the process, a domain vocabulary is also formalized and used by all parties.

Note how at this stage we are not concerned about where these entities come from or how they are passed around in the system.

What's important is that our entities are at the heart of our system, because we need them to solve domain-related problems for our users.

In DDD, a distinction is often made between entities and value objects . For more info, see this thread about Value vs Entity objects on StackOverflow .

Of course, once we start building our app we need to implement those entities and decide where they fit within our architecture.

And this is where the domain layer comes in.

Going forward, we will refer to entities as models that can be implemented as simple classes in Dart.

The Domain Layer

Let's revisit our architecture diagram:

As we can see, the models belong to the domain layer. They are retrieved by the repositories in the data layer below, and can be modified by the services in the application layer above.

So how do these models look like in Dart?

Well, let's consider a Product model class for example:

At a very minimum, this class holds all the properties that we need to show in the UI:

And it also contains fromMap() and toMap() methods that are used for serialization.

There are various ways to define model classes and their serialization logic in Dart. For more info, see my essential guide to JSON parsing in Dart and the follow up article about code generation using Freezed .

Note how the Product model is a simple data classes that doesn't have access to repositories, services, or other objects that belong outside the domain layer.

Business logic in the Model classes

Model classes can however include some business logic to express how they are meant to be modified.

To illustrate this, let's consider a Cart model class instead:

This is implemented as a map of key-value pairs representing the product IDs and quantities of the items that we have added to the shopping cart.

And since we can add and remove items from the cart, it may be useful to define an extension that makes this task easier:

The methods above make a copy of the items in the cart (using Map.from() ), modify the values inside, and return a new immutable Cart object that can be used to update the underlying data store (via the corresponding repository).

If you're not familiar with the syntax above, read: How to update a Map of key-value pairs in Dart .
Many state management solutions rely on immutable objects in order to propagate state changes and ensure that our widgets rebuild only when they should. The rule is that when we need to mutate state in our models, we should do so by making a new, immutable copy .

Testing the Business Logic inside our Models

Note how the Cart class and its MutableCart extension don't have dependencies to any objects that live outside the domain layer.

This makes them very easy to test.

To prove this point, here's a set of unit tests that we can write to verify the logic in the addItem() method:

Writing unit tests for our business logic is not only easy, but adds a lot of value .

If our business logic is incorrect, we are guaranteed to have bugs in our app. So we have every incentive to make it easy to test, by ensuring our model classes don't have any dependencies.

We have discussed the importance of having a good mental model of our system.

We've also seen how to represent our models/entities as immutable data classes in Dart, along with any business logic we may need to modify them.

And we've seen how to write some simple unit tests for that business logic, without resorting to mocks or any complex test setup.

Here are some tips you may use as you design and build your apps:

  • explore the domain model and figure out what concepts and behaviors you need to represent
  • express those concepts as entities along with their relationships
  • implement the corresponding Dart model classes
  • translate the behaviors into working code (business logic) that operates on those model classes
  • add unit tests to verify the behaviors are implemented correctly

As you do that, think about what data you need to show in the UI and how the user will interact with it.

But don't worry about how things connect together just yet. In fact, it's the job of the services in the application layer to work with the models by mediating between the repositories in the data layer and the controllers in the presentation layer.

And that will be the subject of a future article.

Flutter Foundations Course Now Available

I launched a brand new course that covers Flutter app architecture in great depth, along with other important topics like state management, navigation & routing, testing, and much more:

Flutter Foundations Course

Flutter Foundations Course

Learn about State Management, App Architecture, Navigation, Testing, and much more by building a Flutter eCommerce app on iOS, Android, and web.

Invest in yourself with my high-quality Flutter courses.

Flutter & Firebase Masterclass

Flutter & Firebase Masterclass

Learn about Firebase Auth, Cloud Firestore, Cloud Functions, Stripe payments, and much more by building a full-stack eCommerce app with Flutter & Firebase.

The Complete Dart Developer Guide

The Complete Dart Developer Guide

Learn Dart Programming in depth. Includes: basic to advanced topics, exercises, and projects. Fully updated to Dart 2.15.

Flutter Animations Masterclass

Flutter Animations Masterclass

Master Flutter animations and build a completely custom habit tracking application.

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Data domains

  • 5 contributors

Data mesh, at core, is founded in decentralization and the distribution of responsibility to domains. If you truly understand this part of the business, you're best positioned to manage the associated data and ensure its accuracy. This is the principle of domain-oriented data ownership.

To promote domain-oriented data ownership, you need to first make a decomposition of your data architecture. The data mesh founder Zhamak Dehghani promotes the Domain-Driven Design (DDD) approach to software development as a useful method to help you identify your data domains.

The difficulty with using DDD for data management is that DDD's original use case was modeling complex systems in a software development context. It wasn't originally created to model enterprise data, and for data management practitioners its method can be abstract and technical. There's also often a lack of understanding of DDD. Practitioners find its conceptual notions too difficult to grasp, or try to project examples from software architecture or object-oriented programming onto their data landscape. This article provides you with pragmatic guidance and clear vocabulary so you can understand and use DDD.

Domain-Driven Design

Introduced by Eric Evans, Domain-Driven Design is a method for supporting software development that helps describe complex systems for larger organizations. DDD is popular because many of its high-level practices impact modern software and application development approaches for things like microservices.

DDD differentiates between bounded contexts, domains, and subdomains. Domains are problem spaces you want to address. They're areas where knowledge, behavior, laws, and activities come together. You see semantic coupling in domains, behavioral dependencies between components or services. Another aspect of domains is communication. Team members must use a language the whole team shares so everyone can work efficiently. This shared language is called the ubiquitous language or domain language .

Domains are decomposed into subdomains to better manage complexity. A common example of this is decomposing a domain into subdomains that each correspond to one specific business problem as shown in Operationalize data mesh for AI/ML .

Not all subdomains are the same. You can, for example, classify domains to be either core, generic, or supporting. Core subdomains are the most important. They're the secret sauce, the ingredients, that make an organization unique. Generic subdomains are nonspecific, and typically easy to solve with off-the-shelf products. Supporting subdomains don't offer competitive advantage, but are necessary to keep an organization running, and are not usually complex.

Bounded contexts are logical (context) boundaries. They focus on the solution space: the design of systems and applications. It's an area where the alignment of focus on the solution space makes sense. Within DDD, this can include code, database designs, and so on. Between the domains and bounded contexts, there can be alignment, there's no hard rule binding the two together. Bounded contexts are technical by nature and can span multiple domains and subdomains.

Domain modeling recommendations

If you adopt data mesh as a concept for data democratization, and implement the principle of domain-oriented data ownership to increase flexibility, how does this work in practice? What might a transition from enterprise data modeling to domain-driven design modeling look like? What lessons can you take from DDD for data management?

Make a functional business decomposition of your problem spaces

Before allowing your teams to operate their data end to end, look at the scope and understand the problem spaces you're trying to address. It's important to do this exercise first before you jump into the details of a technical implementation. When you set logical boundaries between these problem spaces, responsibilities become clearer and can be better managed.

Look at your business architecture when grouping your problem spaces. Within business architecture, there are business capabilities: abilities or capacities that a business possesses or exchanges to achieve a specific purpose or outcome. This abstraction packs data, processes, organization, and technology into a particular context, in alignment with the strategic business goals and objectives of your organization. A business capability map shows which functional areas are appear to be necessary to fulfill your mission and vision.

You can view the capability decomposition of a fictional company, Tailwind Traders, in the following model.

Business Capability Decomposition

Tailwind Traders needs to master all functional areas listed in the Business Capability Map to be successful. Tailwind Traders must be able to sell tickets as part of Online or Offline Tickets Management Systems, for example, or have Pilots available to fly planes as part of a Pilot Management Program. The company can outsource some activities while keeping others as the core of its business.

What you will observe in practice is that most of your people are organized around business capabilities. People working on the same business capability share the same vocabulary. The same is true of your applications and processes, which are typically well-aligned and tightly connected based on the cohesion of activities they support.

Business capability mapping is a great starting point, but your story doesn't end here.

Map business capabilities to applications and data

To better manage your enterprise architecture, align your business capabilities, bounded contexts, and applications. It's important to follow some ground rules as you do so.

Business capabilities must stay on the business level and remain abstract. They represent what your organization does and target your problem spaces. When you implement a business capability, a realization (capability instance) for a specific context is created. Multiple applications and components work together Within such boundaries in your solution space to deliver specific business value.

Applications and components aligned with a particular business capability stay decoupled from applications that are aligned with other business capabilities, because they address different business concerns. Bounded contexts are derived from and exclusively mapped to business capabilities. They represent the boundary of a business capability implementation, and they behave like a domain.

If business capabilities change, bounded contexts change. You preferably expect full alignment between domains and corresponding bounded contexts, but as you'll learn in later sections, reality sometimes differs from the ideal.

If we project capability mapping to Tailwind Traders, bounded context boundaries and domain implementations might look similar to the following diagram.

Diagram showing bounded contexts

In this diagram, Customer management is built upon subject matter expertise and therefore knows best what data to serve to other domains. Customer management's inner architecture is decoupled, so all application components within these boundaries can directly communicate using application-specific interfaces and data models.

Data products and clear interoperability standards are used to formalize data distribution to other domains. In this approach, all data products also align with the domain and inherit the ubiquitous language, which is a constructed, formalized language agreed upon by stakeholders and designers from the same domain, to serve the needs of that domain.

Extra domains from multiple capability realizations

It's important to acknowledge when working with business capability maps that some business capabilities can be instantiated multiple times.

As an example, Tailwind Traders might have multiple localized realizations (or implementations) of "baggage handling and lost items". Assume one line of their business operates only in Asia. In this context, "baggage handling and lost items" is a capability that is fulfilled for Asia-related airplanes. A different line of business might target the European market, and in this context another "baggage handling and lost items" capability is used. This scenario of multiple instances can lead to multiple localized implementations using different technology services and disjoined teams to operate those services.

The relationship of business capability and capability instances (realizations) is one-to-many. Because of this, you end up with extra (sub-) domains.

Find shared capabilities and watch out for shared data

How you handle shared business capabilities is important. You typically implement shared capabilities centrally, as service models, and provide them to different lines of business. "Customer management" can be an example of this kind of capability. In our Tailwind Traders example, both the Asian and European lines of business use the same administration for their clients.

But how can you project domain data ownership on a shared capability? Multiple business representatives likely take accountability for customers that within the same shared administration.

There's an application domain and a data domain. Your domain and bounded context don't perfectly align from a data product viewpoint. You could conversely argue that there's still a single data concern from a business capability viewpoint.

For shared capabilities like complex vendor packages, SaaS solutions and legacy systems, be consistent in your domain data ownership approach. You can segregate data ownership via data products, which might require application improvements. In our Tailwind Traders "customer management" example, different pipelines from the application domain might generate multiple data products: one data product for all Asia-related customers, and one for all Europe-related customers. In this situation, multiple data domains originate from the same application domain.

You can also ask your application domains to create a single data product that encapsulates metadata for distinguishing data ownership within itself. You could, for example, reserve a column name for ownership, mapping each row to a single specific data domain.

Identify monoliths offering multiple business capabilities

Pay attention also to applications that cater to multiple business capabilities, which are often seen in large and traditional enterprises. In our example scenario, Tailwind Traders uses a complex software package to facilitate both "cost management" and "assets and financing". These shared applications are monoliths that provide as many features as possible, making them large and complex. In such a situation, the application domain should be larger. The same thing applies to shared ownership, in which several data domains reside in an application domain.

Design patterns for source-aligned, redelivery and consumer-aligned domains

When mapping your domains, you'll learn there are different patterns based on the creation, consumption, or redelivery of data. For your architecture, design blueprints for supporting your domains based on their particular characteristics.

Source system-aligned domains

Source system-aligned domains

Source system-aligned domains are aligned with source systems where data originates. These systems are typically transactional or operational.

Your goal is to capture data directly from these golden source systems. Read-optimize data products from your providing domains for intensive data consumption. Facilitate these domains using standardized services for data transformation and sharing.

These services, which include pre-configured container structures, enable your source-oriented domain teams to publish data more easily. It's the path of least resistance with minimal disruption and cost.

Consumer-aligned domains

Consumer-aligned domains

Consumer-aligned domains are the opposite of source-aligned domains. They're aligned with specific end-user use cases that require data from other domains. Customer-aligned domains consume and transform data to fit the use cases of your organization.

Consider offering shared data services for data transformation and consumption to cater to these consuming needs. You could offer domain-agnostic data infrastructure capabilities, for example, to handle data pipelines, storage infrastructure, streaming services, analytical processing, and so on.

Redelivery domains

Re-delivery domains

The reusability of data is a different and more difficult scenario. For example, if downstream consumers are interested in a combination of data from different domains, you can create data products that aggregate data or combine high-level data required by many domains. This allows you to avoid repetitive work.

Don't create strong dependencies between your data products and analytical use cases. Strive for flexibility and loose coupling instead. The following model demonstrates how you can achieve flexibility. A domain takes ownership for both data products and analytical use cases, and has designed separate processes for data product creation and data usage.

Define overlapping domain patterns

Domain modeling often gets complicated when data or business logic is shared across domains. In large-scale organizations, domains often rely on data from other domains. It can be helpful to have generic domains that provide integration logic in a way that allows other subdomains to standardize and benefit from it. Keep your shared model between subdomains small and always aligned with the ubiquitous language.

For overlapping data requirements, you can use different patterns from domain-driven design. Here's a short summary of the patterns you could choose from:

DDD patterns for overlapping domains

  • A separate ways pattern can be used if you prefer the associated cost of duplication over reusability. Reusability is sacrificed for higher flexibility and agility.
  • A customer-supplier pattern can be used if one domain is strong and willing to take ownership of downstream consumers' data and needs. Drawbacks include conflicting concerns and forcing your downstream teams to negotiate deliverables and schedule priorities.
  • A partnership pattern can be used when your integration logic is coordinated in an unplanned manner within a newly created domain. All teams cooperate with and regard each others' needs. Because no one can freely change the shared logic, significant commitment is needed from everyone involved.
  • A conformist pattern can be used to conform all your domains to all requirements. Use this pattern when 1) your integration work is complex, 2) no other parties can have control, or 3) you use vendor packages.

In all cases, your domains should adhere to your interoperability standards. A partnership domain that produces new data for other domains must expose their data products like any other domain, including taking ownership.

Domain responsibilities

Data mesh decentralizes data ownership by distributing it among domain teams. For many organizations, this means a shift from a centralized model around governance to a federated model. Domain teams are assigned tasks, such as:

  • Taking ownership of data pipelines, such as ingestion, cleaning and transforming data, to serve as many data customer's needs as possible
  • Improving Data Quality , including adherence to SLAs and quality measures set by data consumers
  • Encapsulating metadata or using reserved column names for fine-grain row- and column-level filtering
  • Application and source system schema registration
  • Metadata for improved discoverability
  • Versioning information
  • Linkage of data attributes and business terms
  • Integrity of metadata information to allow better integration between domains
  • Adhering to data interoperability standards, including protocols, data formats and data types
  • Providing lineage either by linking source systems and integration services to scanners or by manually providing lineage
  • Adhering to data sharing tasks, including IAM access reviews and data contract creation

Level of granularity for decoupling

Now you know how to recognize and facilitate data domains, you can learn to design the right level of domain granularity and rules for decoupling. Two important dimensions are in play when you decompose your architecture.

Granularity for functional domains and setting bounded contexts is one dimension. Domains conform to a particular way of working, ensuring data becomes available to all domains using shared services, taking ownership, adhering to metadata standards, and so on.

Set fine-grained boundaries where possible for data distribution. Becoming data-driven is all about making data available for intensive reuse. If you make your boundaries too loose, you'll force undesired couplings between many applications and lose data reusability. Strive for decoupling each time data crosses boundaries of business capabilities. Within a domain, tight coupling within the inner architecture of the domain is allowed. However, when crossing the boundaries of business capabilities, domains must stay decoupled and distribute read-optimized data products for sharing with other domains.

Granularity for technical domains and infrastructure utilization is the other important dimension. Your data landing zones enable agility for servicing data applications , which create data products. How do you create this kind of landing zone with shared infrastructure and services underneath? Functional domains are logically grouped together and are good candidates for sharing platform infrastructure. Here are some factors to consider when creating these landing zones:

  • Cohesion and efficiency when working with and sharing data is a strong driver of aligning functional domains to a data landing zone. This relates to data gravity, the tendency to constantly share large data products between domains.
  • Regional boundaries can result extra data landing zones being implemented.
  • Ownership, security, or legal boundaries can force domains segregation. For example, some data can't be visible to any other domains.
  • Flexibility and the pace of change are important drivers. Some domains can have a high velocity of innovation, while other domains strongly value stability.
  • Functional boundaries can drive teams apart. An example of this could be source-oriented and consumer-oriented boundaries. Half of your domain teams might value certain services over others.
  • If you want to potentially sell or separate off your capability, you should avoid integrating tightly with shared services from other domains.
  • Team size, skills and maturity can all be important factors. Highly skilled and mature teams often prefer to operate their own services and infrastructure, while less mature teams are less likely to value the extra overhead of platform maintenance.

Before you provision many data landing zones, look at your domain decomposition and determine what functional domains are candidates for sharing underlying infrastructure.

Business capability modeling helps you to better recognize and organize your domains in a data mesh architecture. It provides a holistic view of the way data and applications deliver value to your business, while at the same time helping you prioritize and focus on your data strategy and business needs. You can also use business capability modeling for more than just data. For example, if scalability is a concern, you can use this model to identify your most critical core capabilities and develop a strategy for them.

Some practitioners raise the concern that building target-state architecture by mapping everything out up front is an intensive exercise. They instead suggest you identify your domains organically while you onboard them into your new data mesh architecture. Instead of defining your target state from the top down, you work bottom-up, exploring, experimenting, and transitioning your current state towards a target state. While this proposed approach might be quicker, it carries significant risk. You can easily be in the middle of a complex move or remodeling operation when things start to break. Working from both directions, top-down and bottom-up, and then meeting in the middle over time is a more nuanced approach.

  • What is a data product?

Was this page helpful?

Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see: https://aka.ms/ContentUserFeedback .

Submit and view feedback for

Additional resources

Vaclav Kosar's face photo

Vaclav Kosar

Boundary control entity architecture pattern.

Boundary-Control-Entity (BCE) is also called Entity-Control-Boundary (ECB), Entity-Boundary-Control (EBC), Hexagonal, Onion, or The Clean Architecture (Clean Code book). Boundary-Control-Entity architecture is effectively directory (package) structure pattern for sorting classes. It is an alternative to Three-tier architecture. It is very simple and natural way to structure classes, which goes well with modularized architecture. One creates one BCE package structure per a microservice, feature or module having ideally between 9 and 30 classes.

BCE Explained

BCE is distinct from the multi-tier architecture in that it does not see some outside systems as backend and some as frontend. It has single category of outside systems.

The package structure is often visualized as tree onion layers, or as a hexagonal layers, where the most outer layer is Boundary, central is Control and inner is Entity.

  • Boundary is an interface to the outside world. It contains classes responsible for all communications with systems outside application runtime. It contains configuration, and establishing external connections. You may choose to place all API objects here as well and separate internal domain objects into entity package to avoid accidentally changing the API.
  • Control represents all logic that doesn’t fit well into Boundary. It contains algorithms, SQL Queries.
  • Entity contains data structures which are allowed to have some behaviour. It contains domain objects with basic functionalities.

Three-tier vs Boundary Control Entity Architecture

Three-tier architecture the much more common than Boundary Control Entity architecture. Three-tier architecture is recommended by Marin Fowler under name of Presentation, Domain, Data . Three-tier architecture is composed of presentation layer, application layer, data access layer. BCE splits domain objects from the data access layer and merges the rest with presentation layer:

  • BCE Domain = domain objects (from the data access layer)
  • BCE Boundary = presentation - data access without domain objects
  • BCE Control = application layer

Boundary-Control-Entity (BCE) can be compared to Model-View-Controller (MVC) architecture.

MVC is composed of:

  • Model: Represents the data and business logic of the system, similar to the Entity component in BCE.
  • View: Represents the presentation layer, responsible for displaying the data from the model to the user. It is similar to the Boundary component in BCE, focusing on user interfaces.
  • Controller: Acts as an intermediary between the model and the view, handling user input and updating the model and view accordingly. It is similar to the Control component in BCE.

Key concepts have analogous function:

  • Model ~ Entity
  • View ~ Boundary
  • Controller ~ Control

View in MVC is specifically user web view in the presentation layer. MVC is more commonly used in web development and user interface design, while BCE can be applied more broadly to various types of software systems, including those without a user interface. BCE focuses more on the system-actor interactions and MVC emphasizing the separation between the presentation layer and the underlying data and logic.

Functionality Folders vs BCE Structure

I am very much for structuring, modularization and encapsulation according to end result features. However, often after separation per major features on sub 30 classes level, I think it is more practical to structure according to technical behaviour. Thus I suggest to use BCE architecture which represents that.

At the same time, don’t be dogmatic and if there are good reasons do what works in the specific case. The BCE could even exist in the sub-folders of a parent folder. The parent folder than can follow service-oriented architecture, where each microservice has its own folder and runtime. Or some of the folder could have to have different names e.g. boundary may have to called “mojo” for example. If BCE doesn’t work for you, let me know!

Dependency Rule

Sometimes an additional dependency rule is being added to BCE: Classes can depend on other classes only within same layer or in one more inner layers. I personally found that this rule impractical mainly thanks to modern tools like annotations. For example annotation on Entity class can reference a serialization class residing in Control.

SQL Queries and Repository Placement Dilemma

Where to place a classes that implement methods to query database? For example Spring Data repository classes in Java? Repositories do define interaction with database. The dataset is an outside system, which would mean we should be placing them into Boundary. But on the other hand they contain application logic in a form of a SQL queries, which should be placed in Control package instead. I place repositories into the Control package. However, code that deals with connections to the database, retries, transactions, database configuration, belongs to the Boundary.

BCE Example

For real world example you can have a look at this version of Gitflow Incremental Builder , which I founded, but which was developed beyond by phenomenal dev Falko . Read more about Gitflow Incremental Builder here . For an overview of boundary control entity architecture folder structure:

  • EventController: REST definition.
  • DBConnector: DB configuration, connection
  • EventTransformer: Transformation between Entity and Payload.
  • EventRepository: DB query definition.
  • EventEntity
  • EventPayload

BCE Quiz Challenge

Retain what you have just read by taking training quiz generated from this article. Boundary-Control-Entity Quiz

Continue: The Cuts Across Your Architecture

Localize Related, Inline over Extract, Specific over Generic BCE does not tell you everything, there are priciples that cuts across the BCE architecture. Learn about code structure principles - Localize Related, Inline over Extract, Specific over Generic .

Other sources

  • Gitflow Incremental Builder uses BCE pattern architecture
  • Down to earth Adam Bien’s video on BCE
  • Uncle Bob’s post - bit more ivory towerish

You'll love also...

Vaclav Kosar's face photo

presentation domain data

Sandro Mancuso

Model-View-Controller (or MVC for short) is one of the most misunderstood design patterns in software design. MVC has its origins in the SmallTalk community in the late 70s but it was only in 1988 that it was expressed as general concept in an article by Glenn E. Krasner and Stephen T. Pope for The Journal of Object Technology .

The main idea behind MVC was to keep the presentation and business logic decoupled from each other.

“MVC”

Up until mid 90s, MVC had mainly been used for desktop and embedded applications. Three-tier architecture (presentation, logic, data) was the reigning architectural style in the 90s, and MVC fitted as a glove. In the late 90s and early 2000s, most companies started migrating their applications to the web and the need to keep the user interface separate from the business logic became even more important. The MVC pattern promotes exactly that and it was a natural step to use MVC for web applications. However, we now had a browser, http calls, and the View would be rendered outside our applications, separately from our Controllers and Model. In order to make things easier for developers, a few frameworks were created. In the Java world, we had MVC Model 1 and Model 2 with Java Server Pages (JSP) and Servlets . Struts , the first major Java MVC framework, was created in the early 2000s. From then onwards, we had many attempts to make the communication between the browser (View) and our applications (Controller and Model) seamlessly. Many other frameworks like Tapestry , Java Server Faces (JSF) and Spring MVC emerged in the Java world. Other languages and platforms also created their own, Rails being one of the most successful ones.

As web applications and frameworks diversified, the MVC pattern also evolved and new variations like MVA , MVP , MVVM and PAC emerged. MVC frameworks became popular and as part of their evolution they tried to automate more and more the manual work the developer had to do, including database access. MVC frameworks started automating how Entities are persisted using Object-Relational Mapping - ORM techniques or closely integrated with other ORM frameworks. Due to the simplicity of many web applications, MVC frameworks made it easy to capture data in a web form and store in relational databases. They also made it easy to read data from the database and display on the browser. The price for this automation was a bastardisation of the Model layer, which became synonymous of Entities that represented tables in the database.

With the Model now associated to Entities and persistency and the View associated to HTML, CSS and JavaScript, developers found no other alternative then to put the business logic of the system in Controller layer. Unfortunately MVC frameworks are one of the main reasons we find Web applications with anaemic domains (Entities with no behaviour) and fat Controllers - responsible for navigation, managing sessions, parsing JSON and XML, and also with business logic.

From the original MVC idea, the Controller layer should be a very thin layer, handling the requests from the View, delegating all the business behaviour to the Model. Depending on the result returned by the Model, the Controller decides which View to display next.

The impacts of the View on the MVC design

In the early days of the web, most MVC frameworks provided a way to generate the View on the backend (server). This was done using some scripting language or template engine. In the Java world we had things like JSP , FreeMarker , Mustache , or Jade . For each request, the server would generate the whole web page and send the full HTML code back to the browser. The UI was quite dumb and the backend was normally stateful, with MVC frameworks keeping track of the HTTP Sessions. This is how the MVC looked liked:

Fully Coupled Server-side MVC

Full server-side MVC

As the web evolved, the need for better usability increased and with it the demand for Rich Internet Applications (RIA) . More and more we started using JavaScript in the front-end in order to have a more dynamic user interface. With this, the View moved to the browser:

Decoupled View MVC

Decoupled View MVC

But that was not enough. We still had problems. The full application had to be redeployed if we wanted to make a change either on the front-end or backend. Scaling stateful backends was expensive. We also had to deploy our applications (at least in the Java world) inside web or application servers . In order to solve that, we had to completely decouple the front-end and backend and ideally, have a completely stateless backend. Front-end technologies like AngularJS and Node.JS allowed us to move both View and Controller to the browser. With this, we could create stateless backend, only providing APIs.

Decoupled Model MVC

Decoupled View MVC

Although we changed were the View and Controller lived over time, where does MVC fit when it comes to the wider architecture of an application?

Domain Driven Design and Layered architecture

In Domain Driven Design(DDD) applications are normally split into 4 layers:

“DDD

  • User Interface (View in MVC) : Also known as Presentation Layer , it is responsible for presenting information and capturing commands of a user or another system.
  • Application Layer (Controller in MVC) : Defines the behaviours the system is supposed to provide and directs the appropriate domain objects to execute those behaviours. This layer is normally kept thin, only coordinating the behaviour of different domain objects that together form a full business feature. This layer does not have state reflecting the system situation but can hold state that reflects the progress of a business feature.
  • Domain Layer (Model in MVC) : Represents concepts of the business, information about the system situation and business rules. Business state is controlled and used here but the technical details of storing it are delegated to the Infrastructure layer. In MVC terms, this is the Model.
  • Infrastructure Layer : Provides generic technical capabilities that support the layers above. Examples would be message sending to the application, persistence, drawing UI components and support the pattern of interactions between the four layers through an architectural framework.

Note: I’ll leave the discussion around Layered and Hexagonal Architecture for a different blog post. For now, let’s just focus on separating concerns and keeping our domain model isolated.

Domain Model

Behaviour and state are both part of the domain model of an application. The domain model is a set of business concepts defined by many different design elements or building blocks . As defined in Domain Driven Design, these design elements are not only Entities but also Services (Application, Domain, Infrastructure), Repositories, Factories, Aggregates, and Value Objects.

Delivery Mechanism

There is a difference between system features and how they are delivered to the external world. We can deliver system features through a Web interface, APIs, mobile clients, messaging, or any other mechanism or protocol. Delivery Mechanism is the name given to the area of our application responsible for providing the business features to the external world. In a web application, the delivery mechanism is normally composed by the View (HTML, CSS, JavaScript) and Controller (code that responds to users interactions).

MVC is a macro pattern that can be used as a good guideline to keep the delivery mechanism decoupled from the domain model. With that in mind, if we superimpose the delivery mechanism, domain model on the MVC structure we will have the following:

“MVC,

When it comes to MVC frameworks, they should be restricted to the View and Controller layers, never the Model.

Reduce coupling avoiding frameworks

Many web frameworks allow us to transform entities into JSON/XML and vice-versa. Some do that via annotations , others do it via reflection or name convention. It is also a common practice to use ORM frameworks to automatically persist and retrieve data from a database using our entities. Similar to the MVC frameworks, we need to add ORM annotations to our entities, or rely on reflection or name convention. With annotations we explicitly make our Model layer to know about the Controller and persistency, causing a circular dependency. If we rely on naming convention or reflection, we have a worse problem because the coupling is still there but is not visible. If we change an Entity, we may not know that we are changing the API or Database.

“JSON

Although reducing boiler plate code using frameworks sounds a great idea, we are coupling the structure of our entities (domain model) to the APIs we provide to the external world and our database. Changing the database impacts the API and vice-versa. What had initially been seen as a time saver, now is a reason to avoid change. The larger and more coupled the system grows, the less developers will be inclined to make changes due to the size of the ripple effects. Many web apps today have APIs which are far from ideal as they reflect the internal data structure of the application instead of focusing only on the information that would make more sense to the external world.

Do not be afraid to create your own mappers. They are normally very simple to write and there are many small libraries specialised to parse objects to JSON or persist data to databases. The advantage of writing our own mappers is that we do not need to couple our APIs or databases to anything. Changes are localised and easy to change. On top of that, we can easily test-drive our mappers and move API tests to the unit level instead of doing it at Acceptance level.

Deployment options

The biggest advantage of keeping the Domain Model decoupled from the Delivery Mechanism is that it give us deployment options as the application evolves.

While the application is small, we can keep the separation between delivery mechanism and domain model just using good package/namespace structure or sub-modules, but still keep everything in the same project and deploy both together, as a single application.

Embedded Domain Model

“Embedded

As the application grows and we want to add different delivery mechanisms, or use different technology stacks, or scale delivery mechanism and domain model in different ways, it makes sense to deploy them individually. If the separation is already there, it would not be so hard to achieve that. We just need to wrap the Domain Model in some infrastructure and change the invocation from the Delivery Mechanism.

Deployable Domain Model

Deployable Domain Model

In MVC, Controllers are very thin classes (just a few lines) which identify the request from the user, invoke the appropriate behaviour in the Model, and invoke the appropriate View once the Model returns. Controllers might also need to deal with code related to the delivery mechanism (HTTP response codes, JSON converters, etc) but most of it should be delegated to other classes which also live in the controller layer. Model is not only about entities (state); it’s about all the behaviour of your system - the entire Domain Model.

The choice of View technology impacts on how your layers are coupled and how your application is deployed.

Keep the Delivery Mechanism decoupled from your Domain Model. The Domain Model should not know anything about how the business feature and data are delivered to the outside world.

Don’t couple all the layers of your application using frameworks that will tell our entities how they should be represented as JSON or persisted in a database. Use small libraries instead of big frameworks. Use a small library at your Controller layer to convert JSON to and from your domain objects and another small library inside your repositories to convert your entities to and from data in the database. If you are using Java, we created LightAccess for this purpose.

Keeping Delivery Mechanism decoupled from Domain Model give us options to deploy and scale the application.

Ready to be inspired?

Join our newsletter for expert tips and inspirational case studies

Related Blogs

Doing Software Modernisation well now will pay dividends down the road

  • By Natalie Gray
  • Posted 03 May 2024

Doing Software Modernisation well now will pay dividends down the road

Is it possible to modernise critical legacy systems whilst maintaining business as usual? This was the theme of a recent techUK panel event where..

  • software modernisation
  • Legacy Code
  • public sector

Fireside Chat #63: Optimising Developer Productivity

  • Posted 26 Mar 2024

Fireside Chat #63: Optimising Developer Productivity

What is developer productivity? Is there an inevitable trade-off between speed and quality? How can organisations foster long-term productivity gains..

  • development team
  • fireside chat
  • Developer experience
  • developer productivity

Advanced TDD learning path

  • By María Dueñas
  • Posted 21 Mar 2024

Advanced TDD learning path

If you have a lot of experience implementing TDD, your learning plan should focus on deepening your understanding of advanced principles, exploring..

  • Test Driven Development
  • Español – América Latina
  • Português – Brasil
  • Tiếng Việt
  • Android Developers

Domain layer

The domain layer is an optional layer that sits between the UI layer and the data layer.

presentation domain data

The domain layer is responsible for encapsulating complex business logic, or simple business logic that is reused by multiple ViewModels. This layer is optional because not all apps will have these requirements. You should only use it when needed-for example, to handle complexity or favor reusability.

A domain layer provides the following benefits:

  • It avoids code duplication.
  • It improves readability in classes that use domain layer classes.
  • It improves the testability of the app.
  • It avoids large classes by allowing you to split responsibilities.

To keep these classes simple and lightweight, each use case should only have responsibility over a single functionality, and they should not contain mutable data. You should instead handle mutable data in your UI or data layers.

Naming conventions in this guide

In this guide, use cases are named after the single action they're responsible for. The convention is as follows:

verb in present tense + noun/what (optional) + UseCase .

For example: FormatDateUseCase , LogOutUserUseCase , GetLatestNewsWithAuthorsUseCase , or MakeLoginRequestUseCase .

Dependencies

In a typical app architecture, use case classes fit between ViewModels from the UI layer and repositories from the data layer. This means that use case classes usually depend on repository classes, and they communicate with the UI layer the same way repositories do—using either callbacks (for Java) or coroutines (for Kotlin). To learn more about this, see the data layer page .

For example, in your app, you might have a use case class that fetches data from a news repository and an author repository, and combines them:

Because use cases contain reusable logic, they can also be used by other use cases. It's normal to have multiple levels of use cases in the domain layer. For example, the use case defined in the example below can make use of the FormatDateUseCase use case if multiple classes from the UI layer rely on time zones to display the proper message on the screen:

presentation domain data

Call use cases in Kotlin

In Kotlin, you can make use case class instances callable as functions by defining the invoke() function with the operator modifier. See the following example:

In this example, the invoke() method in FormatDateUseCase allows you to call instances of the class as if they were functions. The invoke() method is not restricted to any specific signature—it can take any number of parameters and return any type. You can also overload invoke() with different signatures in your class. You'd call the use case from the example above as follows:

To learn more about the invoke() operator, see the Kotlin docs .

Use cases don't have their own lifecycle. Instead, they're scoped to the class that uses them. This means that you can call use cases from classes in the UI layer, from services, or from the Application class itself. Because use cases shouldn't contain mutable data, you should create a new instance of a use case class every time you pass it as a dependency.

Use cases from the domain layer must be main-safe ; in other words, they must be safe to call from the main thread. If use case classes perform long-running blocking operations, they are responsible for moving that logic to the appropriate thread. However, before doing that, check if those blocking operations would be better placed in other layers of the hierarchy. Typically, complex computations happen in the data layer to encourage reusability or caching. For example, a resource-intensive operation on a big list is better placed in the data layer than in the domain layer if the result needs to be cached to reuse it on multiple screens of the app.

The following example shows a use case that performs its work on a background thread:

Common tasks

This section describes how to perform common domain layer tasks.

Reusable simple business logic

You should encapsulate repeatable business logic present in the UI layer in a use case class. This makes it easier to apply any changes everywhere the logic is used. It also allows you to test the logic in isolation.

Consider the FormatDateUseCase example described earlier. If your business requirements regarding date formatting change in the future, you only need to change code in one centralized place.

Combine repositories

In a news app, you might have NewsRepository and AuthorsRepository classes that handle news and author data operations respectively. The Article class that NewsRepository exposes only contains the name of the author, but you want to display more information about the author on the screen. Author information can be obtained from the AuthorsRepository .

presentation domain data

Because the logic involves multiple repositories and can become complex, you create a GetLatestNewsWithAuthorsUseCase class to abstract the logic out of the ViewModel and make it more readable. This also makes the logic easier to test in isolation, and reusable in different parts of the app.

The logic maps all items in the news list; so even though the data layer is main-safe, this work shouldn't block the main thread because you don't know how many items it'll process. That's why the use case moves the work to a background thread using the default dispatcher.

Other consumers

Apart from the UI layer, the domain layer can be reused by other classes such as services and the Application class. Furthermore, if other platforms such as TV or Wear share codebase with the mobile app, their UI layer can also reuse use cases to get all the aforementioned benefits of the domain layer.

Data layer access restriction

One other consideration when implementing the domain layer is whether you should still allow direct access to the data layer from the UI layer, or force everything through the domain layer.

UI layer cannot access data layer directly, it must go through the Domain layer

An advantage of making this restriction is that it stops your UI from bypassing domain layer logic, for example, if you are performing analytics logging on each access request to the data layer.

However, the potentially significant disadvantage is that it forces you to add use cases even when they are just simple function calls to the data layer, which can add complexity for little benefit.

A good approach is to add use cases only when required. If you find that your UI layer is accessing data through use cases almost exclusively, it may make sense to only access data this way.

Ultimately, the decision to restrict access to the data layer comes down to your individual codebase, and whether you prefer strict rules or a more flexible approach.

General testing guidance applies when testing the domain layer. For other UI tests, developers typically use fake repositories, and it's good practice to use fake repositories when testing the domain layer as well.

The following Google samples demonstrate the use of the domain layer. Go explore them to see this guidance in practice:

Recommended for you

  • Note: link text is displayed when JavaScript is off
  • UI State production

Content and code samples on this page are subject to the licenses described in the Content License . Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.

Last updated 2023-07-12 UTC.

COMMENTS

  1. Presentation Domain Data Layering

    Although presentation-domain-data separation is a common approach, it should only be applied at a relatively small granularity. As an application grows, each layer can get sufficiently complex on its own that you need to modularize further. When this happens it's usually not best to use presentation-domain-data as the higher level of modules.

  2. Clean Architecture Guide (with tested examples): Data Flow ...

    Presentation Layer depends on Domain Layer. Domain Layer is the most INNER part of the onion (no dependencies with other layers) and it contains Entities, Use cases & Repository Interfaces. Use cases combine data from 1 or multiple Repository Interfaces. Data Layer contains Repository Implementations and 1 or multiple Data Sources.

  3. Clean Architecture

    The Clean Architecture and the Data Domain Presentation Architecture is pretty popular and I've been asked a lot about it directly and indirectly. I decided ...

  4. Flutter App Architecture: The Presentation Layer

    Flutter App Architecture: The Domain Model. Flutter App Architecture: The Application Layer. And this time, we will focus on the presentation layer and learn how we can use controllers to: hold business logic. manage the widget state. interact with repositories in the data layer. This kind of controller is the same as the view model that you ...

  5. PresentationDomainDataLayering

    When conversation concerning this we can either look at it as one pattern (presentation-domain-data) or shared it into twos patterns (presentation-domain, and domain-data). Both points of view are useful - I think of presentation-domain-data as a compound concerning presentation-domain and domain-data.

  6. Presentation Domain Data Layering / A Complete Overview of the Best

    Although presentation-domain-data separation is a common approach, to should only be applied by a relatively small granularity. As an application grows, each layer can get sufficiently complex on its own that you needed to modularize further. When these befalls it's usually not best to use presentation-domain-data as and higher rank of modules.

  7. MVVM Architecture components with different layers

    Presentation-Domain-Data & Presentation-Data Layer Hello Android Geeks, In this blog, I would like to share my experience on working with Android Architecture Components (LiveData, ViewModel and ...

  8. Clean Architecture with MVVM

    Presentation Layers includes normal activities, fragments, adapters, view models. Domain Layer is a contract between the Data Layer and the Presentation Layer. The domain layer is related to ...

  9. Architecture Learnings Part #3 {What is in a Domain?}

    presentation domain data Defining our Domain Entities - From the problem statement, we can define two entities here viz. User and Article, which can be easily identified by their unique ids and ...

  10. 2023.02

    Presentation Domain Data Layering. One of the most common ways to modularize an information-rich program is to separate it into three broad layers: presentation (UI), domain logic (aka business logic), and data access. So you often see web applications divided into a web layer that knows about handling HTTP requests and rendering HTML, a ...

  11. Visualizing Fowler's Layering Principles

    Always wrap domain logic with a service layer. 4: 5-1: The domain layer should not talk to external systems - the service layer should do that. 2: 3-1: Changing a lower level layer interface should not change upper layer interfaces. 2: 5-3: There are at least three main layer types: presentation, domain, and data source. 3: 9-6

  12. Exploring Clean Architecture in Flutter: Presentation, Data, and Domain

    Domain Layer: The domain layer contains the core business logic of the application. It focuses on the application's core functionalities, such as the quiz logic in our case. Data Layer: The data layer handles data storage, retrieval, and external data source integration. It acts as a bridge between the domain and external data sources.

  13. Why should I isolate my domain entities from my presentation layer?

    (Bob Martin has a rule that's another version of this.) In a system like this, the presentation can change independently of the domain. Such as, for example, a company that maintains prices in Euros and uses French in the company offices, but wants to present prices in dollars with text in Mandarin. The domain is the same; the presentation can ...

  14. Flutter App Architecture: The Domain Model

    Flutter App Architecture using data, domain, application, and presentation layers. The data layer sits at the bottom and contains the repositories that are used to talk to external data sources. Just above it, we find the domain and application layers. These layers are very important as they hold all the models and business logic of our app.

  15. Data domains

    The data mesh founder Zhamak Dehghani promotes the Domain-Driven Design (DDD) approach to software development as a useful method to help you identify your data domains. The difficulty with using DDD for data management is that DDD's original use case was modeling complex systems in a software development context.

  16. design patterns

    From what I have studied thus far my structure looks like this: MVC Internet Application -> The standard internet project - models in here are ViewModels. Domain/Business layer -> business specific classes/models that controllers can use to process domain entities from the data layer before passing on to the relevant views.

  17. Boundary Control Entity Architecture Pattern

    BCE Boundary = presentation - data access without domain objects; BCE Control = application layer; BCE vs MVC. Boundary-Control-Entity (BCE) can be compared to Model-View-Controller (MVC) architecture. MVC is composed of: Model: Represents the data and business logic of the system, similar to the Entity component in BCE. View: Represents the ...

  18. MVC, Delivery Mechanism and Domain Model

    The main idea behind MVC was to keep the presentation and business logic decoupled from each other. Up until mid 90s, MVC had mainly been used for desktop and embedded applications. Three-tier architecture (presentation, logic, data) was the reigning architectural style in the 90s, and MVC fitted as a glove. In the late 90s and early 2000s ...

  19. Clean Architecture in Flutter

    Presentation Layer Structure 2- Domain Layer Responsibility. The Domain Layer, also known as the Business Logic or Use Case Layer, contains the core business rules and logic of the application.

  20. Five Essential Presenting Tips for Data Professionals

    Overview of Your Journey. Why Good Presentations Matter. Tip 1 — Understand Your Audience. Tip 2 — Use Visuals Religiously. Tip 3 — Avoid Jargon and Keep it Simple Silly. Tip 4 — Relate Your Work to the Bigger Picture. Tip 5 — Have a Memorable Bottom Line. Wrapping Up.

  21. Domain layer

    Domain layer. The domain layer is an optional layer that sits between the UI layer and the data layer. Figure 1. The domain layer's role in app architecture. The domain layer is responsible for encapsulating complex business logic, or simple business logic that is reused by multiple ViewModels. This layer is optional because not all apps will ...

  22. What Is Domain Authority And How Can You Improve It?

    Domain authority is a score created by Moz—a provider of search engine optimization (SEO) tools and software—that indicates a website's potential for ranking in search engine result pages ...