Skip to main content

Domain modelling

Entity Builders

If Domain modelling is enabled, the SDK provides Builders for entities. These builders can be easily used to construct entities using the builder pattern which allow to write readable, understandable code to set up complex objects.

Namespace entity builders

  • Each namespace has its own entity builder grouping usually prefixed with the namespace prefix.

  • Namespace with prefix cc would get a CcEntityBuilder class that can be injected and groups all the entity builders in that domain.

For example:


// Import Entity Builder of namespace prefixed by cc
import de.cards.sdk.domain.cc.facade;

// Declaring Entity Builder for namespace cc
@Autowired
protected CcEntityBuilder ccEntityBuilder;

// Using Entity Builder to construct an entity "CreditCard"
CreditCard creditCardEntity = ccEntityBuilder.getCreditCard().setName("John Doe").build();

Domain entity builder

Besides Namespace entity builders, the SDK also provides a DomainEntityBuilder which groups all namespace entity builders of domain namespaces.

💡tip

You can inject the DomainEntityBuilder into your implementation code as shown below. Additionally, in the implementation of services, agents and commands, it is available automatically as entityBuilder, derived from the base class.


// Import DomainEntityBuilder
import de.cards.sdk.domain.facade.DomainEntityBuilder;

// Declare & Inject Domain Entity Builder
@Autowired
private DomainEntityBuilder domainEntityBuilder;

// Use the Domain Entity Builder to create an entity that was modelled in domain namespace
// with prefix: cc
Owner owner = this.domainEntityBuilder.getCc().getOwner().build();

// use default entityBuilder derived from the base class
// Create a customer entity that exists in a domain namespace prefixed by cc.
Customer customer = entityBuilder.getCC().getCustomer()
.setCustomerId("Customer Id")
.setCustomerName("Joh Doe")
.setAmixCard(new AmixCard("some id", CardType.debit))
.build();
💡tip

Implement Domain Services

Every service comes with an automatically generated implementation file, which allows to implement business logic. Domain services can have input and output defined, which needs to be considered during implementation. Typically, a domain service implements logic which loads data from the database, publish events or throws errors.

Service Base

  • For each service there will be an abstract class Service Base generated in the SDK.

  • The Service Base provides access to the repository, the entity builder, the event builder and the event producer.

  • The Service Base contains one abstract method named execute.

  • This execute method needs to be implemented in the generated implementation file for the service.

Input & output entity

  • Service execute method will take a modelled Input Entity as a parameter.

  • Service execute method will return a modelled Output Entity as a return type.

Business errors

  • If a service is modelled with Business Errors, it will be added as throws declaration to service execute method.

Events

  • If a service is modelled with Events, for each event there will be four inherited methods in the Service Base class to publish the event with the following signatures.

    • Event only
    • Event and messageKey
    • Event and messageHeaders
    • Event, messageKey and messageHeaders
  • It is also possible to publish events with any publish method mentioned above using EventProducerService directly.

Implementation example

Example of Balance service implementation file.

//... imports
import myproj.sdk.domain.schemas.SchemaGroup.BalanceCheckedSchema;

@Service
public class BalanceService extends BalanceServiceBase {

private static Logger log = LoggerFactory.getLogger(BalanceService.class);

public BalanceService(DomainEntityBuilder entityBuilder, DomainEventBuilder eventBuilder, EventProducerService eventProducer, Repository repo) {
super(entityBuilder, eventBuilder, eventProducer, repo);
}

// Example of a service implementation logic
@NewSpan
@Override
public Balance execute(CreditCard creditCard) throws CreditCardNotFoundError {

log.info("BalanceService.execute()");

// Use repository to get card instance
Optional<Card> cardRootEntity = this.repo.getCc().getCard().findById(creditCard.getId());
if(cardRootEntity.isPresent()) {

// Use Domain Entity Builder from base class to create an instance of Balance entity
Balance balance = this.entityBuilder.getCc().getBalance().build();
balance.setAvaliableBalance(cardRootEntity.getBalance());
balance.setHoldAmount(cardRootEntity.getHoldAmount());

// Publish event, if the event is using a schema from the schema registry as payload
BalanceCheckedEvent schemaEvent = this.eventBuilder.getCc().getBalanceCheckedEvent().build();

// Create the payload
BalanceCheckedSchema schema = new BalanceCheckedSchema();
schema.setProperty1("value");
schemaEvent.setPayload(schema);

// Publish the event
this.publishBalanceCheckedEvent(schemaEvent);

/**
* Create an event and set payload entity, if the event is using an entity as payload
* only available for event support 1.0
* @deprecated use schemas from the schema registry instead
*/
BalanceUpdatedEvent entityEvent = this.eventBuilder.getCc().getBalanceUpdatedEvent().build();
// Using an entity as event payload
entityEvent.setPayload(balance);
// Publish the event
this.publishBalanceUpdatedEvent(entityEvent);

return balance;

} else {
String errorMessage = String.format("Credit card with id %s not found", creditCard.getId());
throw new CreditCardNotFoundError(errorMessage);
}
}
💡tip

The @NewSpan annotation creates a new span which will be a child of the existing span via the underneath springframework.cloud.sleuth that helps with microservice tracing.

Example for the alternative ways to publish event from a service.

//... imports
import myproj.sdk.domain.schemas.SchemaGroup.BalanceCheckedSchema;

@Service
public class BalanceService extends BalanceServiceBase {

private static Logger log = LoggerFactory.getLogger(BalanceService.class);

@NewSpan
@Override
public Balance execute(CreditCard creditCard) throws CreditCardNotFoundError {

log.info("BalanceService.execute()");

// Create event
BalanceCheckedEvent schemaEvent = this.eventBuilder.getCc().getBalanceCheckedEvent().build();

// Create the payload
BalanceCheckedSchema schema = new BalanceCheckedSchema();
schema.setProperty1("value");
schemaEvent.setPayload(schema);

// Publish the event
this.publishBalanceCheckedEvent(schemaEvent);

// publish the event with custom messageKey
this.publishBalanceCheckedEvent(schemaEvent, "customMessageKey");

// publish the event with messageHeaders
HashMap<String, Object> map = new HashMap();
map.put("headerKey", "headerValue");
MessageHeaders headers = new MessageHeaders(map);
this.publishBalanceCheckedEvent(schemaEvent, headers);

// publish the event with messageKey and messageHeaders
this.publishBalanceCheckedEvent(schemaEvent, "customMessageKey", headers);

/**
* Event 1.0 with entity payload
* @deprecated use schemas from the schema registry instead
*/
BalanceUpdatedEvent entityEvent = this.eventBuilder.getCc().getBalanceUpdatedEvent().build();
// Using an entity as event payload
entityEvent.setPayload(balance);
// Publish the event
this.publishBalanceUpdatedEvent(entityEvent);

// Publish the event with messageHeaders
HashMap<String, Object> map = new HashMap();
map.put("headerKey", "headerValue");
MessageHeaders headers = new MessageHeaders(map);
this.publishBalanceUpdatedEvent(entityEvent, headers);

return balance;
}

warning

Message key will be ignored in case of publishing event 1.0 (Entity payload).

Trigger Services

The generated SDK provides various classes, which allow to easily trigger services from other places in the service.

Directly use Service class

The simplest way of triggering another service is to directly inject and use the generated service class.

In the example below, it is shown how to inject the service class and execute it using an input entity.

// Importing the service class from namespace cc
import de.cards.domain.cc.service.GetCreditCardDetails;

// Declare and Inject class of service GetCreditCardDetails
@Autowired
private GetCreditCardDetails getCreditCardDetails;

// Calling GetCreditCardDetails and passing cardIdentificationEntity as service input entity
CreditCard creditCardEntity = getCreditCardDetails.execute(cardIdentificationEntity);

Namespace Service facade

For each domain namespace, a facade is generated where all the services defined in the namespace are grouped together. The facades can be injected and used as shown in the example below.

In the below example, CcService is a namespace facade that group all the services of the namespaces where cc is the namespace acronym.

// Importing cc namespace service facade that provides access to all of its services.
import de.cards.sdk.domain.cc.facade.CcService;

// Declare and Inject Service Facade
@Autowired
private CcService ccService;

// Use namespace service facade to call GetCreditCardDetails service within namespace cc

// Calling GetCreditCardDetails and passing cardIdentificationEntity as service input entity
CreditCard creditCardEntity = ccService.getCreditCardDetails(cardIdentificationEntity);

General Domain Service facade

Besides Namespace Service facades, the SDK also provides a Service facade which holds all services in domain namespaces.


// Import DomainService
import de.cards.sdk.domain.facade.DomainService;

// Declare & Inject Domain Service Facade
@Autowired
private DomainService domainService;

// Calling GetCustomerDetails service that belongs to namespace prefixed by cc
Customer customerEntity = domainService.getCc().getCustomerDetails(customerIdentificationEntity);

Course Implement Domain Logic