blur-bright-bulb-131023

cropped-button Introduction

I’ve recently read again excellent Implementing Domain Driven Design book by Vaughn Vernon. He presents there a lot of examples for domain objects, services and other components. I spotted a nice pattern that he uses for them – Domain Registry.

I stared to use this pattern in my newest code task.
I want to show you usecases and discuss about this alternative way to resolve dependencies…

cropped-button What is Domain Registry

DomainRegistry in Mr Vernon book was introduced as object, which has access to domain services and repositories. Registry brings clean API to get these objects and invoke some operations on them in another domain objects.

Let’s look at the example from Vernon code:

public class DomainRegistry implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    public static AuthenticationService authenticationService() {
        return (AuthenticationService) applicationContext.getBean("authenticationService");
    }

    public static AuthorizationService authorizationService() {
        return (AuthorizationService) applicationContext.getBean("authorizationService");
    }

    public static EncryptionService encryptionService() {
        return (EncryptionService) applicationContext.getBean("encryptionService");
    }

    public static GroupMemberService groupMemberService() {
        return (GroupMemberService) applicationContext.getBean("groupMemberService");
    }

    public static UserRepository userRepository() {
        return (UserRepository) applicationContext.getBean("userRepository");
    }

    @Override
    public synchronized void setApplicationContext(
            ApplicationContext anApplicationContext)
    throws BeansException {

        if (DomainRegistry.applicationContext == null) {
            DomainRegistry.applicationContext = anApplicationContext;
        }
    }
}

 

As we can see, he implemented Springs’ ApplicationContextAware interface, and injected ApplicationContext (which holds every application Spring bean) into this implementation. There is not a lot of logic. Each method retrieves Spring bean from ApplicationContext.

Example of DomainRegistry usage in Mr Vernon code:

public void testAuthenticationTenantFailure() throws Exception {

        User user = this.userAggregate();

        DomainRegistry
            .userRepository()
            .add(user);

        UserDescriptor userDescriptor =
            DomainRegistry
                .authenticationService()
                .authenticate(
                        DomainRegistry.tenantRepository().nextIdentity(),
                        user.username(),
                        FIXTURE_PASSWORD);

        assertNotNull(userDescriptor);
        assertTrue(userDescriptor.isNullDescriptor());
    }

 

Here, in test DomainRegistry provides domain service (AuthenticationService) and repository (UserRepository) to other objects.

cropped-button Passing dependencies as params

I found this pattern quite useful. I’ve been recently working on code trying to solve some problem. I’m creating web app with domain related to tickets reservations for many events.

I have an aggregate called Reservation, which is created per User – represented by userId, and Event – represented by eventId. Website sends a request to my application with create Reservation task with given userId and eventId.

Reservation has its own static factory method for creation – called create.
Create method must do following things:

  • Check whether given params – userId and eventId are not null
  • Check if there is already Reservation for given userId and event Id. This should happen with usage of ReservationRepository.
  • Check if there is Event with given eventId in datastore. For this task, Reservation factory method should user EventRepository.
  • Use Clock domain class to obtain current time.

As we can see, Reservation create factory method needs 2 domain repositories and Clock domain service to perform it’s task. Not to mention about two required parameters – userId and eventId. That’s gives us five method parameters! Uncle Bob would be proud…

But ok… let’s look at the ReservationService code with injected depencencies:

@Service
@Transactional
@RequiredArgsConstructor
public class ReservationService {

    private final ReservationRepository reservationRepository;
    private final EventRepository eventRepository;
    private final Clock clock;
    
    public ReservationId createReservation(UserId userId, EventId eventId) {
        Reservation reservation = Reservation.create(userId, eventId, reservationRepository, eventRepository, clock);
        reservationRepository.save(reservation);

        return reservation.getId();
    }

}

 

And create static factory method of Reservation aggregate:

/**
     * Creates Reservation for user with given UserId and event with given EventId.
     * Reservation is valid for 10 minutes.
     * After that time will expire.
     */
    static Reservation create(UserId userId, EventId eventId, ReservationRepository reservationRepository, EventRepository eventRepository, Clock clock) {
        checkNotNull(userId);
        checkNotNull(eventId);

        reservationRepository
                .findCurrentByUserIdAndEventId(userId, eventId)
                .ifPresent(r -> {
                    throw new ReservationAlreadyExistException();
                });

        Event event = eventRepository
                .findById(eventId)
                .orElseThrow(() -> new IllegalArgumentException(format("Event with given id %s not found", eventId)));

        LocalDateTime started = clock.whatDateTimeIsIt();

        if (started.isBefore(event.getSalesStartDate()) || started.isAfter(event.getSalesEndDate())) {
            throw new UnableToCreateReservationForEventException();
        }

        return new Reservation(
                ReservationId.generate().getValue(),
                userId,
                eventId,
                ReservationDateTimeInfo.from(started, event.getSalesEndDate()),
                CURRENT
        );

cropped-button Getting rid of a lot of params

If Reservations’ create method with required 5 params (3 dependencies) did not frighten you, just remember, that this creation process was not that complicated.
There will be more complex processes which will require more and more dependencies to play with. So I imagine that there may be methods with 4-5 dependencies or more.
Just think about unit testing that kind of things…

Of course we could wrap all incoming parameters into one data transfer object, but for me this would add additional, not necessary noise in code.
It would be look like this:

@Service
@Transactional
@RequiredArgsConstructor
public class ReservationService {

    private final ReservationRepository reservationRepository;
    private final EventRepository eventRepository;
    private final Clock clock;

    public ReservationId createReservation(UserId userId, EventId eventId) {
        Reservation reservation = Reservation.create(
                new ReservationCreationData(
                        userId,
                        eventId,
                        reservationRepository,
                        eventRepository,
                        clock
                )
        );
        reservationRepository.save(reservation);

        return reservation.getId();
    }
    
    @Value
    static class ReservationCreationData {
        private final UserId userId;
        private final EventId eventId;
        private final ReservationRepository reservationRepository;
        private final EventRepository eventRepository;
        private final Clock clock;
    }

}

 

Reservation:

/**
 * Creates Reservation for user with given UserId and event with given EventId.
 * Reservation is valid for 10 minutes.
 * After that time will expire.
 */
static Reservation create(ReservationCreationData creationData) {
    UserId userId = creationData.getUserId();
    EventId eventId = creationData.getEventId();

    checkNotNull(userId);
    checkNotNull(eventId);

    creationData.getReservationRepository()
            .findCurrentByUserIdAndEventId(userId, eventId)
            .ifPresent(r -> {
                throw new ReservationAlreadyExistException();
            });

    Event event = creationData.getEventRepository()
            .findById(eventId)
            .orElseThrow(() -> new IllegalArgumentException(format("Event with given id %s not found", eventId)));

    LocalDateTime started = creationData.getClock().whatDateTimeIsIt();

    if (started.isBefore(event.getSalesStartDate()) || started.isAfter(event.getSalesEndDate())) {
        throw new UnableToCreateReservationForEventException();
    }

    return new Reservation(
            ReservationId.generate().getValue(),
            userId,
            eventId,
            ReservationDateTimeInfo.from(started, event.getSalesEndDate()),
            CURRENT
    );
}

 

This is better than 5 parameters approach. We have only one parameter now. But this generates some new inconvenience. For me this code is hard to read.

Additionally it requires transport wrapping object for almost all nontrivial operations. Now we have one. What happens when our app will have one hundred of non trivial operations?

cropped-button DomainRegistry on the way

For me DomainRegistry implementation from Mr Vernon code has some disadvantages.
Firstly, it has static methods – which can be hard for unit testing and mocking.
The second disadvantage is extending Spring ApplicationContextAware interface and interfere Spring core code with clean domain.

Instead of creating class with static methods, domain has only knowledge of this interface:

public interface DomainRegistry {

    Clock clock();

    EventRepository eventRepository();

    ReservationRepository reservationRepository();

}

 

Thanks to Dependency injection and Dependency inversion principle, Spring detailed implementation of this interface may lay far far away from clean Domain. In other words – Domain doesn’t know what is the algorithm for retrieving domain services.
We are trying make Domain framework agnostic as much as can.

DomainRegistry implementation lays in infrastructure scope (with package scope visibility):

@Component
@RequiredArgsConstructor
class DomainRegistryImpl implements ApplicationContextAware, DomainRegistry {

    private ApplicationContext context;

    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public Clock clock() {
        return context.getBean(Clock.class);
    }

    @Override
    public EventRepository eventRepository() {
        return context.getBean(EventRepository.class);
    }

    @Override
    public ReservationRepository reservationRepository() {
        return context.getBean(ReservationRepository.class);
    }
}

 

Let’s refactor ReservationService and inject only DomainRegistry:

@Service
@Transactional
@RequiredArgsConstructor
public class ReservationService {

    private final DomainRegistry domainRegistry;

    public ReservationId createReservation(UserId userId, EventId eventId) {
        Reservation reservation = Reservation.create(userId, eventId, domainRegistry);
        domainRegistry
                    .reservationRepository()
                    .save(reservation);

        return reservation.getId();
    }

}

 

And then Reservation create factory method. Not Factory method takes 3 params – two values and one dependency:

static Reservation create(UserId userId, EventId eventId, DomainRegistry registry) {
    checkNotNull(userId);
    checkNotNull(eventId);

    registry.reservationRepository()
            .findCurrentByUserIdAndEventId(userId, eventId)
            .ifPresent(r -> {
                throw new ReservationAlreadyExistException();
            });

    Event event = registry.eventRepository()
            .findById(eventId)
            .orElseThrow(() -> new IllegalArgumentException(format("Event with given id %s not found", eventId)));

    LocalDateTime started = registry.clock().whatDateTimeIsIt();

    if (started.isBefore(event.getSalesStartDate()) || started.isAfter(event.getSalesEndDate())) {
        throw new UnableToCreateReservationForEventException();
    }

    return new Reservation(
            ReservationId.generate().getValue(),
            userId,
            eventId,
            ReservationDateTimeInfo.from(started, event.getSalesEndDate()),
            CURRENT
    );
}

 

For me this approach is the best so far. Of course someone may complain that when we pass DomainRegistry to method, we do not know which dependencies are being used and what is going under the hood. But… isn’t it a purpose of encapsulation?

Method signatures should be verbose enough – they take DomainRegistry object, so they may use it.

What is more – DomainRegistry should be registered per Bounded Context so other Bounded Contexts’ objects shouldn’t be able to use domain dependencies which don’t belong to them.

cropped-button Conclusion

DomainRegistry is nice concept which can be used per Bounded Context.

Advanced domain objects such Aggregates, Domain services or Factories can perform complex operations, which sometimes requires many dependencies. These dependencies can be injected on object creation (for Factories or Domain Services), or passed as method parameter (for Aggregates).

DomainRegistry solve problem of passing too many parameters to domain object methods and prevent to make their contract huge and ugly.

Of course, there are some minor issues of this approach. Because DomainRegistry provide many dependencies, they must be public (even when we want to make them hidden in package scope for example).

But remember… there are no silver bullets.