- Notifications
You must be signed in to change notification settings - Fork5
A Quarkus extension for easy implementation of domain-driven design
License
MatthiasSchupp/ddq
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Quarkus extension for domain-driven Microservices.
- Following Domain-driven design principles:The extension offers different features to support the development of Microservices according to domain-driven designfollowing the bookImplementing Domain-Driven Design fromVaughn Vernon.
- All optional:It is an extension, not a framework. With it you can use the different features you like, but you are not forced to.
To benefit from this extension it is necessary to understand the principles and tactical patterns of domain-driven design.
To create a Quarkus application using this extension and get the right configuration and plugins, the archetype can beused. There is also an example application in the repository to provide some code.
mvn archetype:generate -DarchetypeGroupId=eu.domain-driven -DarchetypeArtifactId=ddq-archetype -DarchetypeVersion=<version>The extension ensures the publishing, collection and correct processing of domain events, also with multiple instancesof the same service running at the same time.To make this possible, each event will first written to the database and then processed from there. This ensures thedecoupling and transactional security of event processing.
In CDI beans:
@DependendpublicclassExampleService {@InjectEventPublishereventPublisher;publicvoiddoBusinessStuff(StuffIdstuffId) {// Do some stuff hereeventPublisher.publish(newStuffDone(stuffId)); }}
In Pojo classes:
publicclassExample {publicvoiddoBusinessStuff(StuffIdstuffId) {// Do some stuff hereEvents.publish(newStuffDone(stuffId)); }}
In the domain model:
@javax.persistence.EntitypublicclassExampleextendsEntity {@EmbeddedprivateStuffIdstuffId;publicvoiddoBusinessStuff() {// Do some stuff herepublishEvent(newStuffDone(stuffId)); }}
In CDI beans:
@DependendpublicclassExampleService {voidonStuffDone(@ObservesStuffDonestuffDone) {// Do some other stuff here }}
All events published in the application and collected from other applications are available via REST as notifications
http://hostname[:port]/example/resources/notifications/eventsTo collect events from other applications using this extension, an event source must be defined in theapplication.properties file.Valid url formats are:
- hostname[:port]/example
- hostname[:port]/example/resources/notifications/events
- http://hostname[:port]/example
- http://hostname[:port]/example/resources/notifications/events
For an event source, a start id can be defined, which sets the oldest event to collect. Without a start id, thecollection begins with the actual newest event.
quarkus.ddq.event.event-source.<name>.uri=hostname/examplequarkus.ddq.event.event-source.<name>.start-id=0#optional
To provide a better insight in the errors occurred during of the processing of the application than simply write it in anunstructured logfile,the extension provides the errors similar to the events.
In CDI beans:
@DependendpublicclassExampleService {@InjectErrorPublishererrorPublisher;publicvoiddoBusinessStuff(StuffIdstuffId) {// Do some stuff hereerrorPublisher.business("Error Message");try {// Do some stuff here }catch (SomeExceptione) {errorPublisher.technical("Error Message",e); } }}
In Pojo classes:
publicclassExample {publicvoiddoBusinessStuff(StuffIdstuffId) {// Do some stuff hereErrors.business("Error Message",this.getClass());// orErrors.publisher(this.getClass()).business("Error Message");try {// Do some stuff here }catch (SomeExceptione) {Errors.technical("Error Message",this.getClass(),e); } }}
There are some base classes available to easily implement the domain model:
- ValueObject
- UUIDValueObject
- IdentifiedValueObject
- IdentifiedDomainObject
- Entity
HATEOAS REST endpoint with ETag support can be implemented as follows:
@BasePath(GreetingsResource.PATH)publicclassGreetingRepresentationimplementsRepresentation {privatefinalGreetingIdgreetingId;privatefinalPersonperson;privatefinalIntegersalutes;@JsonbTransientprivatefinalStringpersonName;@BaseLink(rel ="self",path ="{greetingId}")privateLinkselfLink;@BaseLink(rel ="person",queryParams =@QueryParam(name ="name",values ="{personName}"))privateLinkpersonLink;@BaseLink(rel ="salute",path ="{greetingId}/salute",condition ="maxSalutesNotReached")privateLinksaluteLink;@BaseLink(rel ="salutes",path ="{greetingId}/salutes")privateLinksalutesLink;publicGreetingRepresentation(Greetinggreeting) {this.greetingId =greeting.greetingId();this.person =greeting.person();this.salutes =greeting.salutes();this.personName =person.name(); }publicbooleanmaxSalutesNotReached() {returnsalutes <100; }@Overridepublicbooleanequals(Objecto) {if (this ==o)returntrue;if (o ==null ||getClass() !=o.getClass())returnfalse;GreetingRepresentationthat = (GreetingRepresentation)o;returngreetingId.equals(that.greetingId) &&person.equals(that.person) &&salutes.equals(that.salutes); }@OverridepublicinthashCode() {returnObjects.hash(greetingId,person,salutes); }}
publicclassGreetingLogRepresentationextendsLogRepresentation {@BaseLink(path ="greetings/salutes")privateLinksalutes;publicGreetingLogRepresentation(Collection<Greeting>greetings) {super(greetings,"greetings",GreetingRepresentation::new); }}
@Path(GreetingsResource.PATH)@Produces(MediaType.APPLICATION_JSON)@Consumes(MediaType.APPLICATION_JSON)@TransactionalpublicclassGreetingsResource {publicstaticfinalStringPATH ="greetings";@InjectGreetingServicegreetingService;@InjectEntityTagResponseFactoryresponseFactory;@GETpublicResponsegreetings(@QueryParam("name")StringpersonName) {returnpersonName !=null ?greetingService.greeting(newPerson(personName)) .map(greeting ->responseFactory.createResponse(Collections.singletonList(greeting),GreetingLogRepresentation::new)) .orElse(Response.status(NOT_FOUND).build()) :responseFactory.createResponse(greetingService.greetings(),GreetingLogRepresentation::new); }@GET@Path("{id}")publicResponsegreeting(@PathParam("id")Stringid) {returngreetingService.greeting(newGreetingId(id)) .map(greeting ->responseFactory.createResponse(greeting,GreetingRepresentation::new)) .orElse(Response.status(NOT_FOUND).build()); }}
About
A Quarkus extension for easy implementation of domain-driven design
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.