DDD – Aggregate Roots and Domain Events publication

Implementing DDD in a traditional object-oriented style *, when an action over an Aggregate succeeds the state of the aggregate changes and one or multiple domain events are published.
A Domain Event represents something that has happened in the domain. In the other hand if the action cannot be fulfilled a Domain Error is thrown as an exception by the aggregate.

Publishing Domain Events from an Aggregate
The question that arises here is Should the aggregates publish events directly?. We can find a multitude of articles and examples where the Aggregate implementation does exactly that. The very same Vaughn Vernon’s IDDD book shows this approach (page 300):

public class BacklogItem extends ConcurrencySafeEntity {
  ...
  public void commitTo(Sprint aSprint) {
  ...
    DomainEventPublisher
      .instance()
      .publish(new BacklogItemCommitted(
         this.tenantId(),
         this.backlogItemId(),
         this.sprintId()));
    }
  ...
}

the BacklogItem Aggregate creates and dispatches an event directly using a DomainEventPublisher instance before the database transaction is commited.

This approach can become quite complex to implement right, because of the transactional concerns. Think about it, if the aggregate publishes events directly, what happens if later on the persistence operation fails?
Unit testing of aggregates gets also complicated, the aggregate is coupled to a DomainEventPublisher that it instantiates directly. At the same time, we know that we should avoid dependency injection into an Aggregate.

An alternative approach is to use a Two-steps event publication

  • 1st) Aggregate root creates and registers domain events for publication.
  • 2nd) When the application Service persists the changes to the aggregate (calling the save method in a Repository) the infrastructure takes care of publishing the registered events only if the save transaction succeeds.

Let’s see this in action with some code. The domain example is for a Runbook which tracks Tasks, and the use case is assigning a task to an operator.

– Service retrieves and acts on the aggregate.

// RunbookService
fun assignTask(c: AssignTask) {
    val runbook = runbookRepository.findById(c.runbookId)

    runbook.assignTask(c.taskId, c.assigneeId, c.userId) // act on the aggregate
    ...

– Aggregate changes its state and then creates and registers domain events.

// Runbook
fun assignTask(taskId: String, assigneeId: String, userId: String) {
    validateIsOwner(userId)

    getTask(taskId).assign(assigneeId)

    registerEvent(TaskAssigned(runbookId, taskId, assigneeId)) // create and register event
}

Notice that Domain Events are only registered by the aggregate root because it is the one ensuring the integrity of the aggregate as a whole.

The way that the aggregate “registers” events is keeping track of them internally as a list of unpublished domain events.

...
@Transient
private val domainEvents = ArrayList()

protected fun registerEvent(event: EventType) {
    this.domainEvents.add(event)
}
...

– Service calls repository to save changes
After calling the assignTask action on the aggregate the service saves the changes (the “changed” aggregate) using the repository.

// RunbookService
fun assignTask(c: AssignTask) {
    val runbook = runbookRepository.findById(c.runbookId)

    runbook.assignTask(c.taskId, c.assigneeId, c.userId)

    runbookRepository.save(runbook) // save aggregate
}

– Infrastructure publishes all the aggregate unpublished domain events.
This can be done directly with a custom repository implementation and some form of internal pub-subscribe mechanism or with the help of a framework. Later on, I describe how to do this for repositories based on Spring Data.

Unit Testing
Unit testing of the aggregate remains simple with this design, there is no need to mock an event publisher or anything else. Asserting that the aggregate created and registered the right events can be done directly checking the aggregate domain events collection.

    @Test
    fun `can assign task`() {
        val runbook = Runbook(RUNBOOK_ID, RUNBOOK_NAME, OWNER_ID)
        runbook.addTask(TASK_ID, TASK_NAME, TASK_DESCRIPTION)

        runbook.assignTask(TASK_ID, TASK_ASSIGNEE_ID, OWNER_ID)

        assertThat(runbook.domainEvents()).contains(TaskAssigned(RUNBOOK_ID, TASK_ID, TASK_ASSIGNEE_ID))
    }

Domain Events
Here is an example of how the Domain Events for an aggregate look using Kotlin sealed classes.

sealed class RunbookEvent

data class TaskAdded(
        val runbookId: String,
        val taskId: String) : RunbookEvent()

data class TaskAssigned(
        val runbookId: String,
        val taskId: String,
        val assigneeId: String) : RunbookEvent()
...

Using Spring Data infrastructure to Publishing Events from Aggregate Roots
Spring Data repositories have support for aggregate domain events publication. On a call to a repository save() method, Spring will look into the passed aggregate for any unpublish domain events and will automatically publish them via its ApplicationEventPublisher API.

To expose the events the aggregate root needs to provide an accessor method annotated with the @DomainEvents annotation.

...
@Transient
private val domainEvents = ArrayList()
 
@DomainEvents
fun domainEvents(): Collection<EventType> {
    return Collections.unmodifiableList<EventType>(domainEvents)
}
...

Aggregate Root base class
We can reuse across all aggregates the same code for registering events placing it in an AggregateRoot base class.

abstract class AggregateRoot<EventType> {

    @Transient
    private val domainEvents = ArrayList<EventType>()

    /**
     * All domain events currently captured by the aggregate.
     */
    @DomainEvents
    fun domainEvents(): Collection<EventType> {
        return Collections.unmodifiableList<EventType>(domainEvents)
    }

    /**
     * Registers the given event for publication.
     */
    protected fun registerEvent(event: EventType): EventType {
        this.domainEvents.add(event)
        return event
    }

    /**
     * Clears all domain events currently held. Invoked by the infrastructure in place in Spring Data
     * repositories.
     */
    @AfterDomainEventPublication
    protected fun clearDomainEvents() {
        this.domainEvents.clear()
    }
}

Conclusion
Domain Events make explicit what changes within a domain. Use them not only to have a richer model but also a better design, implementation and tests. The different parts of the system can be decoupled much easily and prevent us from having large application services or aggregates.
It also simplifies future integration with other systems, translation from Domain Events to messaging Integration Events is trivial.

References
– Implementing Domain Driven Design, Vaughn Vernon.
– A better domain events pattern, Jimmy Bogard. https://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern
– Spring – Domain event publication from aggregate roots, Mark Paluch. https://spring.io/blog/2017/01/30/what-s-new-in-spring-data-release-ingalls#domain-event-publication-from-aggregate-roots
– Domain Events vs. Integration Events in Domain-Driven Design and microservices architectures, Cesar de la Torre
https://blogs.msdn.microsoft.com/cesardelatorre/2017/02/07/domain-events-vs-integration-events-in-domain-driven-design-and-microservices-architectures/
– Domain events: design and implementation. https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/domain-events-design-implementation

1 – In a functional DDD style the aggregates: do not mutate state; do not publish or register events; do not throw exceptions. Instead, they return a result type containing the new version of the aggregate and domain events or in case of failure, the result contains the domain errors.

Advertisements

Angular pluggable architecture?

Pluggable Architecture – A Software Architecture that allows to plug functionality using PluggableModules. Ward Cunningham

One of the projects I am involved in these days is composed of multiple applications each of them with its own front-end implemented in Angular, all using consistent UX patterns and framework. The common user flows like managing projects or navigating to the applications is handled in a central application. At the same time, common components like navbar or sidebars are reused across.

This front-end separation by applications or large features has prevented us from creating a big monolithic web app and our teams have the kind of independence and control in the front-end we get in the back-end with micro-services.

All good so far, but let’s say that now we want to have a dashboard application composed of widgets and one of the requirements is that multiple teams can implement and deploy their own widgets independently. Hence, without the need of modifying the dashboard application code or redeploying it when a new widget comes or gets updated.

Can an Angular CLI application load and render external widgets at runtime?

The short answer is yes, it is possible. You can find a working demo in this repo https://github.com/paucls/angular-pluggable-architecture

The long answer is that there is not a straightforward solution ;-), it took me a little bit of research to find out how.

First of all, the application needs a way to load remote widget bundles at runtime. This sounds like lazy loading, but that is not going to help us, lazy loading is for loading modules linked to child routes, but the modules still need to belong to the same application and be known at build-time.

The solution is to use SystemJS, but only at runtime and only to load the widgets. We do not need to configure our CLI project to switch from Webpack to SystemJS.

Loading a bundle will look like this:

const module = await SystemJS.import('/path-to-a-module-bundle');

The next problem is that the bundle contains a Module and a Component for the widget but they have dependencies themselves to other modules. The smallest component has at least dependencies to @angular/core.

import { Component, OnInit } from '@angular/core';

To stop SystemJS from trying to find and fetch those vendor modules from the network we need to set SystemJS with the modules already available.

/**
 * Set existing vendor modules into SystemJS registry.
 * This way SystemJS won't make HTTP requests to fetch imported modules
 * needed by the dynamicaly loaded Widgets.
 */
import { System } from 'systemjs';
declare const SystemJS: System;

import * as angularCore from '@angular/core';
import * as angularCommon from '@angular/common';
import * as angularCommonHttp from '@angular/common/http';

SystemJS.set('@angular/core', SystemJS.newModule(angularCore));
SystemJS.set('@angular/common', SystemJS.newModule(angularCommon));
SystemJS.set('@angular/common/http', SystemJS.newModule(angularCommonHttp));

The second part is compiling and rendering each widget. For this, we need to make use of the Angular JIT compiler to compile the Angular module and component of the widget. And then create the widget component in ViewChield target div.

@Component({
  selector: 'app-dashboard',
  template: '<div #content></div>'
})
export class DashboardComponent implements AfterViewInit {

  @ViewChild('content', { read: ViewContainerRef }) content: ViewContainerRef;

  constructor(private compiler: Compiler, private dashboardService: DashboardService,
    private injector: Injector) { }

  ngAfterViewInit() {
    this.loadWidgets();
  }

  private async loadWidgets() {
    const widgets = await this.dashboardService.getWidgetConfigs().toPromise();
    widgets.forEach((widget) => this.createWidget(widget));
  }

  private async createWidget(widget: WidgetConfig) {
    // import external module bundle
    const module = await SystemJS.import(widget.moduleBundlePath);

    // compile module
    const moduleFactory = await this.compiler.compileModuleAsync(module[widget.moduleName]);

    // resolve component factory
    const moduleRef = moduleFactory.create(this.injector);
    const componentProvider = moduleRef.injector.get(widget.name);
    const componentFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(componentProvider);

    // compile component
    this.content.createComponent(componentFactory);
  }

}

Note that to be able to use the JIT compiler our build needs to have AOT disabled what it is not ideal. There is an open issue (https://github.com/angular/angular/issues/20875) to allow the use of the compiler with AOT, I tried some of the temporal solutions suggested like defining custom providers for JIT in the app module but I could not make it work yet.

References:
– Developing with Angular, Denys Vuika. https://github.com/DenisVuyka/developing-with-angular/tree/master/angular/plugins
– As busy as a bee — lazy loading in the Angular CLI, David Herges https://blog.angularindepth.com/as-busy-as-a-bee-lazy-loading-in-the-angular-cli-d2812141637f … how does lazy loading in the Angular CLI work under the hood?
– How to load dynamic external components into Angular application? https://stackoverflow.com/questions/45503497/how-to-load-dynamic-external-components-into-angular-application/45506470#45506470
– Here is what you need to know about dynamic components in Angular, Maxim Koretskyi https://blog.angularindepth.com/here-is-what-you-need-to-know-about-dynamic-components-in-angular-ac1e96167f9e
– Modules are not what you think they are, Maxim Koretskyi https://youtu.be/pERhnBBae2k
– Extension mechanism demo https://github.com/maximusk/extension-mechanism-demo
– JIT Compiler needed with AOT Build for Dynamic Component. https://github.com/angular/angular/issues/20875

Implementing Consumer-Driven Contract tests in Angular

Overview

Consumer-driven means that the Consumer is responsible for specifying the interactions that it expects with the provider. Interactions are described in detail: HTTP verb, endpoint path, params, response status code, resource fields used, etc. and all of them together form the consumer contract.

Consumer contract tests exercise the Client code

We do not define “hand-craft” directly the contracts because they could get out of sync quickly and not reflect the real interaction that the consumer code performs. Instead what we do is unit test the layer of our consumer that interacts with the provider (the Client or Adapter code) using the Pact JS library to specify the interactions. If the unit tests pass a Pact contract file is generated with the contract between consumer and provider.

Implementing Consumer Contract tests with Pact

In an Angular front-end application, Consumer Contract tests are implemented using Jasmine and the Pact JS library.
They are similar to other unit tests for logic making HTTP requests. The difference is that instead of mocking the network responses using the Angular MockBackend

mockBackend.connections.subscribe(connection => {
connection.mockRespond(new Response(new ResponseOptions({body: RELEASE_TOGGLES})));
});

we allow the requests to go through the network against the pact-mock-service which verifies and replies according the expected interaction.

  describe('Delete Task', () => {

    beforeAll((done) => {
      provider.addInteraction({
        state: 'a task with id task-id exists',
        uponReceiving: 'a request to delete task task-id',
        withRequest: {
          method: 'DELETE',
          path: '/tasks/task-id'
        },
        willRespondWith: {
          status: 204
        }
      }).then(done, e => done.fail(e));
    });

    it('should call the API to delete the task', (done) => {
      tasksService.delete('task-id').then(done);
    });

  });

Flexible Matching

It is vital that tests specify which are the resource fields the UI app cares about. But to not make tests rigid we’ll usually assert only the name and type of each field, not their exact content. To help with this, the Pact JS library provides flexible matching by regular expressions or type.

Next test is an example of a GET interaction where the expected resource and fields are defined using the type matcher Pact.Matchers.somethingLike.

  describe('Get all Tasks', () => {

    beforeAll((done) => {
      provider.addInteraction({
        state: 'tasks exists',
        uponReceiving: 'a request to get tasks',
        withRequest: {
          method: 'GET',
          path: '/tasks'
        },
        willRespondWith: {
          status: 200,
          headers: {'Content-Type': 'application/json'},
          body: [{
            id: Pact.Matchers.somethingLike('an id'),
            name: Pact.Matchers.somethingLike('a name'),
            done: Pact.Matchers.somethingLike(false),
            userId: Pact.Matchers.somethingLike('an user id')
          }]
        }
      }).then(done, e => done.fail(e));
    });

    it('should return all tasks from API', (done) => {
      const tasks: Task[] = [{
        id: 'an id',
        name: 'a name',
        done: false,
        userId: 'an user id'
      }];

      tasksService.getAll().then(response => {
        expect(response).toEqual(tasks);
        done();
      });
    });
  });

The mock server will return [{id: 'an id’, name: 'a name’, priority: 2, done: false}] in the consumer tests, but when the Pact verification is run in the provider, it will just check that the type of the name value is a String, and that the type of the priority value is a Number, etc.

To reiterate, the resource returned by the provider probably has other properties, but we should only describe the ones that the consumer uses.

Guidelines for state and uponReceiving descriptions

When we define the expected interactions between a consumer and a provider the ‘state’ and ‘uponReceiving’ descriptions used are important and should be written in a consistent style.

Later on, on the provider side, Pact uses the uponReceiving description to name the auto-generated test, and the state description is used to find a correspondent test state.

// Expected Interaction
provider.addInteraction({
  state: 'tasks exists',
  uponReceiving: 'a request to delete that tasks',
  withRequest: { method: 'DELETE', path: '/tasks/task-id' },
  willRespondWith: { status: 204 }
});

But the contracts are not only used by the tests, we also intend them to be user-friendly. Developers can consult contracts in the Pact Broker who formats them in a nice documentation style.

Sample scenarios

Note that the state affects what the producer should do and the uponReceiving affects the request that the consumer makes

Successful interaction where data is returned
state: ‘tasks for project with id project-id exist’,
uponReceiving: ‘a request to get tasks for that project’

Successful interaction where no data is returned
state: ‘tasks for project with id project-id do not exist’,
uponReceiving: ‘a request to get tasks for that project’

404 error interaction
state: ‘project project-id does not exist’,
uponReceiving: ‘a request to get tasks for that project’

Domain validation error interaction
state: ‘a task task-id exists and domain rules exist that prevent its modification’,
uponReceiving: ‘a request to update that task’

Successful interaction where data is created
state: ‘a project with name project-1 does not exist’,
uponReceiving: ‘a request to create a project with name project-1’

Pact spec files and tests setup

The contract tests files should be placed next to the code in the same way we do for the unit tests and use .pact.spec.js as file extension.

...
import { TestBed, getTestBed } from '@angular/core/testing';
import { HttpModule } from '@angular/http';
import * as Pact from 'pact-web';

describe('TasksService', () => {

  let provider;
  let tasksService;

  beforeAll((done) => {
    provider = Pact({ // 1)
      consumer: 'task-list-ui',
      provider: 'task-list-api',
      web: true
    });

    // required for slower CI environments
    setTimeout(done, 200);

    // Required if run with `singleRun: false`
    provider.removeInteractions();
  });

  afterAll((done) => {
    provider.finalize().then(done, e => done.fail(e));
  });

  beforeEach(() => {
    TestBed.configureTestingModule({  // 2)
      providers: [TasksService],
      imports: [HttpModule]
    });

    tasksService = getTestBed().get(TasksService);
  });

  afterEach((done) => {
    provider.verify().then(done, e => done.fail(e));
  });

Notes:

  1. Instantiates a mock provider, indicating the name of the consumer app, the name of the provider server and the port of the pact-mock-service.
  2. Injecting the subject under test TasksService. In contract tests we use real HttpModule instead a stub one with MockBackend, it allows HTTP interactions to pass through to the pact mock server.

For any producer we interact, we should configure a karma proxy mapping. This is to redirect requests from our pact tests to the pact-mock-service running on a different port. This allows us to use partial URLs in our app code.

// karma.conf.js
proxies: {
  '/tasks': 'http://localhost:1234/tasks'
},

Running Consumer Contract tests

To run the tests use:
npm test

If the tests pass, the Pact-Mock-Service generates the pact contract files in the projects /pacts folder.

Those pacts, in a CDC testing spirit, can be used now to drive the development of the provider.

Implementing API client code

Client code should be minimal, acting as a facade that simplifies the usage of the provider API to the angular service layer hiding the HTTP details. If your angular service is simple, only dealing with the HTTP calls, you don’t need anything more. In the other hand if you have complex services I recommend to extract that responsibility to a specific Client or Adapter class.

Examples

In the next repository, you can find an example of consumer contract tests for the interactions between a task-list-ui app and a REST API backend.
https://github.com/paucls/task_list_ui-angular/blob/feature/pact-consumer-contract-tests/src/app/tasks-list/tasks.client.pact.spec.ts

To configure an Angular CLI project to support Pact tests check my previous article.

Documentation

https://github.com/pact-foundation/pact-js

Configure Pact JS Consumer Contract tests in Angular

This post describes the steps to configure Karma on an Angular 4+ project to run consumer contract tests using Pact.

1) Install pact and pact-web
yarn add pact –dev
yarn add pact-web –dev

Pact JS is a javascript library to create consumer-driven contract tests.
Pact-web is a module to support Pact in non-Node environments such as Karma.

2) Install Karma Pact
yarn add @pact-foundation/karma-pact –dev

Under the covers karma-pact will be in charge of starting a pact-mock-service in the port localhost:1234 before the tests execution as well as shutting it down when karma finish.

3) Configure Karma to support consumer contract tests
karma.conf.js

frameworks: ['jasmine', '@angular/cli', 'pact'],   // 1) add pact framework
plugins: [
  require('karma-pact'), // 2) add karma-pact plugin
  ...
]
...
pact: [{cors: true, spec: 2, port: 1234, dir: 'pacts/'}],  // 3) configure one or multiple pact-mock-services here
proxies: { // 4) here we can define proxies to redirect requests from our pact tests to the mock server
  '/api/tasks': 'http://localhost:1234/api/tasks'
},

Notice: In some situations, it may be useful to run multiple Pact Mock Servers. For instance, when running tests with karma, if a consumer defines contracts for several providers, the generated Pact contracts may be mixed up. A solution to solve this is to run one Pact Mock Server per provider. Support for multiple services was introduced on karma-pact v1.0.0.

After a successful consumer contract test run, Pact generates contract files and saves them in the /pacts folder.

Run consumer contract tests

At this stage, the project is configured and ready to run contract tests along the rest of unit tests. Tests can be run as usual with
ng test

You can find a working example of this configuration in the next sample app:
https://github.com/paucls/task_list_ui-angular/tree/feature/pact-consumer-contract-tests

In my next article, I explain in more detail how to implement Consumer-Driven Contracts.

Documentation
https://github.com/pact-foundation/pact-js#using-pact-in-non-node-environments
https://github.com/pact-foundation/pact-js/issues/20
https://github.com/pact-foundation/karma-pact
https://github.com/koelec/angular-pact-example
https://github.com/NomiJ/Pact-Angular2-Example