Factories

A factory is responsible for creating a whole, internally consistent aggregate when it is too complicated to do it in a constructor.

Characteristics

Objects produced

A factory is part of the domain and responsible for creating some domain objects. In the business framework a factory can only create domain objects implementing Producible:

  • Aggregates,
  • Value objects,
  • Domain events.

Note that non-root entities are not produced by factories but should be created by their aggregate root.

Identity creation

Being responsible for creating valid aggregates, factories may need to create their identity. This can be done from input parameters given to the factory or by using a generation mechanism. This mechanism can be automated by the business framework, see below.

Default factory

The business framework provides a default factory for each class implementing Producible that does not already has a custom factory. To use a default factory, inject the Factory interface, with the type to create as generic parameter:

public class SomeClass {
    @Inject
    private Factory<SomeAggregate> someAggregateFactory;
    
    public void someMethod() {
        SomeAggregate someAggregate = factory.create(new Name("John Doe"));
    }
}

The create(...) method can take any argument(s), and will:

  1. Try to find a constructor on the produced class that unambiguously matches the given argument types,
  2. Invoke this constructor if found or throw an exception if not,
  3. Use the IdentityService to generate an identity on the produced object if necessary.

Custom factory

If you need custom creation logic, it is necessary to define a custom factory.

Declaration

To declare a factory, create an interface extending Factory, in the aggregate package, with at least one method for creating the produced object:

public interface SomeFactory extends Factory<SomeAggregate> {
    SomeAggregate createFromName(String name);
}

Then implement this interface in a class extending BaseFactory:

public class SomeFactoryImpl extends BaseFactory<SomeAggregate> implements SomeFactory {
    SomeAggregate createFromName(String name) {
        SomeAggregate someAggregate = new SomeAggregate(new Name(name));
        someAggregate.initialize(new Date());
        return someAggregate;
    }
}

If the implementation does not depend upon technical aspects like a library, put it in the same package as the interface, otherwise move the implementation in the infrastructure package.

Usage

To use your custom factory, inject the custom interface:

public class SomeClass {
    @Inject
    private SomeFactory someFactory;
    
    public void someMethod() {
        SomeAggregate someAggregate = someFactory.createFromName("someName");
        // do something with aggregate
    }
}

Identity generation

Declaration

The business framework provides an identity generation mechanism. To use it, annotate the field of the aggregate root holding the identity with @Identity and an optional qualifier to select the correct implementation:

public class SomeAggregate extends BaseAggregateRoot<UUID> {
    @Identity(generator = SequenceGenerator.class)
    @InMemory
    private Long id;
}

Usage

To automatically trigger the identity generation mechanism at the end of a creation method, annotate it with the @Create annotation:

public class SomeFactoryImpl extends BaseFactory<SomeAggregate> implements SomeFactory {
    @Create
    SomeAggregate createFromName(String name) {
        SomeAggregate someAggregate = new SomeAggregate(new Name(name));
        someAggregate.initialize(new Date());
        return someAggregate;
    }
}

As an alternative you can inject the generated identity programmatically by using the IdentityService:

public class SomeClass {
    @Inject
    private IdentityService identityService;

    public void someMethod() {
        SomeAggregate someAggregate = new SomeAggregate(new Name(name));
        identityService.identify(someAggregate);
        return someAggregate;
    }
}

Note that identity generation does not walk the object graph to generate identities for potential sub-entities. You must trigger identity generation (automatically or manually) separately on each entity.

Class configuration

To avoid specifying the generator qualifier in code, you can specify it as the defaultGenerator key in class configuration:

classes:
  org:
    mycompany:
      myapp:
        domain:
          model:
            someaggregate:
              defaultGenerator: org.seedstack.business.util.inmemory.InMemory

The defaultGenerator property expects either:

  • A qualifier annotation class name (like @InMemory),
  • Or an arbitrary string which will be used as the parameter of the @Named qualifier.

Built-in identity generators

Sequence

The sequence generator provides a unique ever-incrementing number. Numbers are not required to be contiguous. Implementations of this strategy must implement the SequenceGenerator interface.

An in-memory implementation is provided by InMemorySequenceGenerator. Its qualifier is @InMemory.

Other implementations may be found in the add-ons library.

UUID

The UUID generator uses a Universally Unique Identifier as identity. Implementations of this generator must implement the UuidGenerator interface.

An implementation that uses the UUID Java class is provided by SimpleUuidGenerator. Its qualifier is @Named("simpleUUID").

Custom identity generator

You can define your own custom generator. Either write :

  • A direct implementation of IdentityGenerator,
  • An implementation of an existing generator interface,
  • Or an interface extending IdentityGenerator and an implementation of this custom interface.

As an example of the second option, consider the code below:

@Named("timestamp")
public class TimestampIdentityGenerator implements SequenceGenerator<Long> {
    @Override
    public <E extends Entity<Long>> Long generate(Class<E> entityClass) {
        return new Date().getTime();
    }
}

Example

The identity generator

public class OrderIdGenerator implements IdentityGenerator<OrderId> {
    @Override
    public <E extends Entity<OrderId>> OrderId generate(Class<E> entityClass) {
        // custom logic to generate order identifiers
    }
}

The aggregate

public class Order extends BaseAggregateRoot<OrderId> {
    @Identity(generator = OrderIdGenerator.class)
    private final OrderId id;
    
    // ...
}

The factory interface

public interface OrderFactory extends GenericFactory<Order> {
    Order createOrder(Customer customer, List<Pair<ProductId, Integer>> orderedProducts);
    
    Order createRepeatOrder(Order previousOrder);
    
    Order createCreditOrder(Order orderToCredit);    
}

The factory implementation

@Create
public class OrderFactoryImpl extends BaseFactory<Order> implements OrderFactory {
    @Override
    public Order createOrder(CustomerId customerId) {
        return new Order(customerId);
    }
    
    @Override
    public Order createRepeatOrder(Order previousOrder) {
        Order order = new Order(previousOrder.getCustomerId());
        previousOrder.items().forEach(order::addItem);
        return order;
    }
    
    @Override
    public Order createCreditOrder(Order orderToCredit) {
        return new CreditOrder(orderToCredit.getCustomerId(), orderToCredit.getId());
    }
}
   

On this page


Edit