Blog

FIS-SST

Domain-driven design code architecture for Blazor WebAssembly

Blazor WebAssembly 3.2.0 has been shipped by Microsoft in May 2020, but so far there are lots of articles for this framework. It is developed with high speed by Microsoft and by open-source communities. Blazor WebAssembly is a framework for writing frontend applications with HTML/CSS and C# in browsers supporting WebAssembly. WebAssembly is a platform for running binary compiled code, for example C# CLR [1].

Domain-driven design (DDD) is an approach to software development that centers the development on programming a domain model that has a rich understanding of the processes and rules of a domain. DDD introduces Ubiquitous Language that embeds domain terminology into the software systems, that we build, and Bounded Contexts, which are a way of dealing with large models. Each Bounded Context has internal model and explicit relationships with other Bounded Contexts. DDD also introduces a way of classifying objects into Entities, Value Objects, Services, Factories and Aggregates [2, 3].

In this article we propose a code architecture for Blazor WebAssembly with DDD approach.

Usual DDD software architecture

Usual DDD code architecture composes of 4 layers:

  1. User interface,
  2. Application,
  3. Infrastructure,
  4. Domain.

Architecture diagram is shown on Figure 1. For a better understanding of the presented mechanisms, an example of a domain in the field of retail trade was used.

User interface composes of frontend applications, desktop, browser apps and of Web API applications. User interface only knows about Application and usually uses DTOs to communicate with Application layer.

Application layer contains DTOs, Commands, Queries, Command handlers, Query handlers and Validators. Commands and Queries are parameter classes that are sent to Application and handled by Command or Query handler. Command handlers can manipulate data in application and Query handlers can only get data and cannot change anything in the underlying state. Application takes data from infrastructure and executes logic on Domain with it. Infrastructure is a layer that contains databases, migrations, repositories, I/O logic and integrations

 

Figure 1 Domain-driven design usual architecture.

Domain is the most important layer in DDD approach. It consists of Bounded Contexts which in code architecture are different solution projects. Each Bounded Context is a separate module with separate logic and code. Communication between different Bounded Contexts is done with Shared Bounded Contexts. Usually each Bounded Context contains Entities, Services, Factories, Value Objects, Aggregates, Validators, Enums and Helpers (i.e. extension methods or exceptions).

Within the analyzed example, there can be distinguished:

  1. Sales Bounded Context,
  2. Inventory Bounded Context.

Both contexts focus on the same concept, but from the perspective of different points of view, which implies different attributes and the logic that is related. Also both contexts communicate through Shared Bounded Context. Example usual architecture for Domain layer is shown on Figure 2.

 

Figure 2 Usual architecture in domain-driven architecture.

Usual Blazor WebAssembly architecture

When creating Blazor WebAssembly application in Visual Studio we already have three projects: Client, Server and Shared. Both Client and Server depend on Shared project and share its code base. Shared project DDLs are copied both to browser client machine and hosting server. This means that backend consists of Server and Shared and frontend consists of Client and Shared. This architecture is shown on Figure 3.

 

Figure 3 Blazor WebAssembly simple architecture.

Proposed domain-driven design architecture for Blazor WebAssembly applications

While merging those two architectures into one we can propose architecture presented on Figure 4. Its main purpose is to keep two main attributes of both architectures: exposing Domain as the most important part (from DDD) and keeping as most as possible shared code between Client and Server (from Blazor WebAssembly). The Shared layer was decomposed into two layers: Shared Application and Shared Domain. All previous DDD layers are now dependent on adequate Shared layers. Backend consists of all layers except Blazor Client. Frontend consists of Shared Domain, Shared Application and Blazor Client. Blazor Client depends both on Shared Domain and Shared Application because it needs both DTOs, Commands, Queries and Enums.

 

Figure 4 Proposed Blazor WebAssembly architecture.

Code structure

Component dependencies and content

Shared Domain:

  1. No dependencies.
  2. Doesn’t contain Entities and Aggregates because Client logic should only use DTOs and not Domain objects.
  3. Contains code used both in Blazor Client and Domain:
    1. enums,
    2. helpers (i.e. exceptions, extensions).

Shared Application:

  1. Depends on Shared Domain.
  2. Contains code used both in Blazor Client and Application:
    1. DTOs,
    2. commands,
    3. queries,
    4. factories for creating DTO,
    5. validators,
    6. helpers (i.e. exceptions, extensions).

Domain:

  1. Depends on Shared Domain.
  2. Each project is one Bounded Context.
  3. Contains code:
    1. database entities,
    2. factories,
    3. aggregates,
    4. value objects,
    5. services,
    6. domain validators,
    7. helpers (i.e. extensions and exceptions).

Application:

  1. Depends on Domain, Infrastructure, Shared Domain, Shared Application.
  2. Contains code:
    1. command handlers,
    2. query handlers.

Infrastructure:

  1. Depends on Shared Domain and Domain (using common enums and Entities)
  2. Contains code:
    1. databases connection logic,
    2. repositories,
    3. code first migrations,
    4. REST API clients,
    5. I/O services,
    6. integration logic with external services,
    7. helpers (i.e. extensions and exceptions).

Server:

  1. Depends on Application and Shared Application.
  2. Contains code:
    1. API controllers.

Blazor Client:

  1. Depends on Shared Domain and Shared Application.
  2. Contains code:
    1. components,
    2. pages,
    3. API services,
    4. Javascript and CSS static files.

Namespace naming convention

Firstly, we set some convention for namespaces naming. It should be:

In our case it is for example:

Inner namespace naming should be focused on code type that is inside namespace and then on functionality:

An example:

Dependency injection

In each project, there is folder Configurations which holds ProjectNameDependencyInjection.cs. Each file contains code that is responsible for handling dependency injection services registration in their project scope. Dependent projects call this code and thanks to it there is no repeated code in the solution. Also adding new client project only requires calling dependent projects dependency injection code. An example of InfrastructureDependencyInjection.cs code is presented below:

Solution in Visual Studio

On Figure 5 whole solution structure is presented in Visual Studio Solution Explorer. Each project is in Solution Folder, so it can be sorted automatically with numbers 1, 2, 3… Each Solution Folder has name of layer. Each project name in solution follows namespace convention with name of hold layer and code type in the end. 

 

Figure 5 Whole solution structure in Visual Studio.

In Figure 6 we can see folder structure for Shared Domain, Shared Application and Infrastructure projects.

Shared Domain contains Enums, Helpers, Services and Validators. Enums hold all important enums for domain. Validators hold classes that are responsible for validating objects, they are being used in Blazor forms and before executing domain logic.

Shared Application contains mostly DTOs, Commands and Queries which are shared between Blazor Client, Server and Application layers for sending objects data via network.

Infrastructure project holds code connected to databases, file systems and external services.

 

Figure 6 Folder structure of Shared and Infrastructure projects.

Each domain Bounded Context has the same structure. Difference from usual DDD structure is that enums and validators are moved to Shared Domain and Domain depends on Shared Domain.

 

Figure 7 Folder structure of Domain projects.

Application layer is left without DTOs, Commands and Queries and contains only code for handling requests from users: Query handlers and Command handlers. Server only has API controllers and configuration code for running Web API. Blazor Client is composed of ApiServices, Components and Pages. ApiServices are services responsible for fetching and saving data to Web API. Components are classes from which Blazor Client pages are created. From Pages each page of the site is created, and they manage site navigation URLs. In case of very large applications, ApiServices or components can be extracted to separate projects.

 

Figure 8 Folder structure of Application and User Interface projects.

Summary

Presented architecture separates domain logic from all unnecessary distraction code, provides loose coupling and maintains focus on important parts of code. Shared part of code was handled by splitting into two layers for more decoupling. More decoupling can be created by adding layers for Shared Components and API Services. There is a possibility to create multiple projects in one layer if the code gets very large. At first glance, the presented architecture may seem complex and complicated for beginner programmers. However, the benefits of separating business processes from technology and sharing certain components between the server and the client causes that the architecture will certainly be appreciated in the case of complex systems.

Bibliography

[1] https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-now-available/

[2] https://martinfowler.com/bliki/DomainDrivenDesign.html

[3] Domain-Driven Design: Tackling Complexity in the Heart of Software, Evans. E, 2003.

 

Contact form

Patrycja Pałus

Office Administrative Assistant

Please feel free to contact me by phone or email

Newsletter FIS-SST

To continue downloading please subscribe to our newsletter 

Newsletter FIS-SST

Aby kontynuować ściąganie prosimy zapisz się do naszego newsletter`a