I have an application that tracks the state of external documents locally through integration events. Some external document types aren't shared and some are. When handling an external event such as DocumentTypeChanged
I have to execute logic similar to:
internalTypeId = internalDocumentTypeFrom(event.newTypeId);
shared = isSharedType(event.newTypeId);
if (internalTypeId == UNKNOWN && shared) trowMissingSharedDocTypeMappingError();
if (documentTracked(event.documentId)) {
changeDocumentType(event.documentId, internalTypeId, event.id);
if (!shared) removeDocument(event.documentId, event.id);
} else if (shared) {
applicationId = mostRecentApplicationIdOfBn(event.businessNumber)
attachDocument(applicationId, event.documentId, event.id, ...);
}
External documents are always supposed to change in reaction to external events only and the ExternalDocument
AR always holds onto the latest externalEventId
for that document.
Where should I place the above logic? Right now the logic is all in the integration's event handler and delegates commands such as changeDocumentType
to the application's layer, but I feel most of the BL is creeping out of the domain.
When looking at IDDD samples, most integration handlers such as this one are translating events to application's service layer commands. It's a bit what I currently have, but I feel that poorly communicates the fact that these commands can only happen in reaction to external events.
Another idea I had was to do something like externalDocumentAppService.handleDocumentTypeChange(externalDocumentId, externalTypeId, externalEventId, bn, occurredOn)
and then the application's service method may look like:
class ExternalDocAppService {
handleDocumentTypeChange(externalDocumentId, externalTypeId, externalEventId, bn, occurredOn) {
internalTypeId = internalDocumentTypeFrom(externalTypeId);
shared = isSharedType(event.newTypeId);
document = documentRepository.findByExternalId(externalDocumentId);
if (document) {
document.handleTypeChange(internalTypeId, shared, externalEventId, occurredOn);
} else if (shared) {
application = caseRepository.mostRecentApplicationOfBn(bn);
document = application.attachDocument(externalDocumentId, ...);
}
if (document) documentRepository.save(document);
}
}
class ExternalDocument {
…
handleTypeChange(internalTypeId, shared, externalEventId, occurredOn) {
if (internalTypeId == UNKNOWN && shared) trowMissingSharedDocTypeMappingError();
this.typeId = internalTypeId;
this.latestExternalEventId = externalEventId;
this.latestExternalChangeDate = occurredOn;
if (!shared) this.remove(...);
}
}
Looking at the code there would still be some business logic in the application's layer, such as knowing to ignore new unshared documents. Could we justify extracting the application's service logic in a domain service here, where the application service would simply delegate to the domain service (more or less)?
I'm also unsure about reactive command names such as handleTypeChange
. Would something like changeTypeBecauseOfExternalTypeChange
be more suitable? Anyway, I'm looking for some design guidance here… thanks!