Add files via upload

initial commit
This commit is contained in:
sbawa
2019-09-23 14:01:55 -07:00
committed by GitHub
commit 4b1bf0962d
13 changed files with 410 additions and 0 deletions

66
Readme.md Normal file
View File

@@ -0,0 +1,66 @@
This projects helps to build and deploy a microservice by applying
DDD, CQRS, Event Souring principles on AXON JVM stack.
<h2>What you'll build</h2>
You'll build a sample microservice with one bounded context.
<h2> What you'll need </h2>
AXON stack - framework and server <br/>
Java <br/>
SpringBoot <br/>
Postgres <br/>
Learn more about AXON stack<br/>
AXON is a open source stack to develop java based microservices . https://axoniq.io
<h2>package structure</h2>
This stack requires you to architect/structure your code is a particular way.
<h3>api</h3>
This is where the core APIs live.
APIs are exposed as command and queries which a client can use to interact
with the system.
This package also contains the Domain Events leveraged by commands and queries
<h3>command</h3>
This package contains model which serves the command side of application.
These model serves the commands only.
<h3>query</h3>
This package contains model which serves the query side of application.
These model serves the queries only.
<h3>client</h3>
This package contains a domain service for test purposes.
----
set up
----
For this DEMO, I am using the Event store which comes as part of AXON stack.
1. spin up the docker container instance of AXON server :
docker run -it --rm --name axonserver -p 8024:8024 -p 8124:8124 axoniq/axonserver
Check AXON dasboard : http://localhost:8024/
2. spin up the docker container instance of postgres :
docker run -it --rm --name postgres -p 5432:5432 -e POSTGRES_USER=planmatch -e POSTGRES_PASSWORD=secret postgres:9.6
----
run application
----
Run this springboot app from PlanMatchService.
----
verify
----
----
Summary
----
Congratulations on spinning up your microservices with AXON stack.

52
pom.xml Normal file
View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>planmatchservice</artifactId>
<version>1.0</version>
<name>leadgen</name>
<description>Demo AXON based microservice for LeadGen using DDD, CQRS, EventSourcing</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>4.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,13 @@
package com.example.match;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class PlanMatchService {
public static void main(String[] args) {
SpringApplication.run(PlanMatchService.class, args);
}
}

View File

@@ -0,0 +1,20 @@
package com.example.match.api;
import lombok.Value;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
import java.util.UUID;
/**
* @author simar bawa
*/
@Value
public class InquiryCreateCommand {
@TargetAggregateIdentifier
UUID id;
String firstName;
String status;
}

View File

@@ -0,0 +1,18 @@
package com.example.match.api;
import lombok.Value;
import java.util.UUID;
/**
* @author simar bawa
*/
@Value
public class InquiryCreatedEvent {
UUID id;
String firstName;
String status;
}

View File

@@ -0,0 +1,20 @@
package com.example.match.api;
import lombok.Value;
import org.axonframework.modelling.command.TargetAggregateIdentifier;
import java.util.UUID;
/**
* @author simar bawa
*/
@Value
public class InquiryScoreCommand {
@TargetAggregateIdentifier
UUID id;
Integer score;
String status;
}

View File

@@ -0,0 +1,18 @@
package com.example.match.api;
import lombok.Value;
import java.util.UUID;
/**
* @author simar bawa
*/
@Value
public class InquiryScoredEvent {
UUID id;
Integer score;
String status;
}

View File

@@ -0,0 +1,15 @@
package com.example.match.api;
import lombok.Value;
import java.util.UUID;
/**
* @author simar bawa
*/
@Value
public class InquirySummaryQuery {
UUID id;
}

View File

@@ -0,0 +1,43 @@
package com.example.match.client;
import com.example.match.api.InquiryCreateCommand;
import com.example.match.api.InquiryScoreCommand;
import com.example.match.query.InquirySummaryView;
import com.example.match.api.InquirySummaryQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.axonframework.messaging.responsetypes.ResponseTypes;
import org.axonframework.queryhandling.QueryGateway;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.UUID;
/**
* @author simar bawa
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class DomainService implements CommandLineRunner{
private final CommandGateway commandGateway;
private final QueryGateway queryGateway;
public void run(String ... args) throws Exception {
UUID id = UUID.randomUUID();
log.debug("issuing create command");
commandGateway.sendAndWait(new InquiryCreateCommand(id, "simar", "create"));
log.debug("issuing score command");
commandGateway.sendAndWait(new InquiryScoreCommand(id, 5, "complete"));
log.debug("querying inquiry");
InquirySummaryView summary = queryGateway.query(new InquirySummaryQuery(id), ResponseTypes.instanceOf(InquirySummaryView.class)).join();
log.debug("inquiry queried {}", summary);
}
}

View File

@@ -0,0 +1,63 @@
package com.example.match.command;
import com.example.match.api.InquiryCreateCommand;
import com.example.match.api.InquiryCreatedEvent;
import com.example.match.api.InquiryScoreCommand;
import com.example.match.api.InquiryScoredEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.modelling.command.AggregateLifecycle;
import org.axonframework.spring.stereotype.Aggregate;
import java.util.UUID;
/**
* @author simar bawa
*/
@Aggregate
@RequiredArgsConstructor
@Slf4j
//@Profile("command")
public class Inquiry {
@AggregateIdentifier
UUID id;
String firstName;
Integer score;
String status;
@CommandHandler
public Inquiry(InquiryCreateCommand cmd) {
log.debug("handling cmd {}", cmd);
if (cmd.getFirstName().equalsIgnoreCase("invalid")) throw new IllegalArgumentException("name is invalid");
AggregateLifecycle.apply(new InquiryCreatedEvent(cmd.getId(), cmd.getFirstName(), cmd.getStatus()));
}
@CommandHandler
public void handle(InquiryScoreCommand cmd) {
log.debug("handling cmd {}", cmd);
if (cmd.getScore() <= 0) throw new IllegalArgumentException("score <= 0");
AggregateLifecycle.apply(new InquiryScoredEvent(cmd.getId(), cmd.getScore(), cmd.getStatus()));
}
@EventSourcingHandler
public void on(InquiryCreatedEvent evt) {
log.debug("applying evt {}", evt);
id = evt.getId();
firstName = evt.getFirstName();
status = evt.getStatus();
}
@EventSourcingHandler
public void on(InquiryScoredEvent evt) {
log.debug("applying evt {}", evt);
score = evt.getScore();
status = evt.getStatus();
}
}

View File

@@ -0,0 +1,45 @@
package com.example.match.query;
import com.example.match.api.InquiryCreatedEvent;
import com.example.match.api.InquiryScoredEvent;
import com.example.match.api.InquirySummaryQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.eventhandling.EventHandler;
import org.axonframework.queryhandling.QueryHandler;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
/**
* @author simar bawa
*/
@Component
@RequiredArgsConstructor
@Slf4j
//@Profile("query")
public class InquirySummaryProjector {
private final EntityManager entityManager;
@EventHandler
public void on(InquiryCreatedEvent evt) {
log.debug("projecting evt {}", evt);
entityManager.persist(new InquirySummaryView(evt.getId(), evt.getFirstName(), evt.getStatus(), evt.getStatus(), 0));
}
@EventHandler
public void on(InquiryScoredEvent evt) {
log.debug("projecting evt {}", evt);
InquirySummaryView summary = entityManager.find(InquirySummaryView.class, evt.getId());
summary.score = evt.getScore();
summary.currentState = evt.getStatus();
}
@QueryHandler
public InquirySummaryView handle(InquirySummaryQuery qry) {
return entityManager.find(InquirySummaryView.class, qry.getId());
}
}

View File

@@ -0,0 +1,27 @@
package com.example.match.query;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.UUID;
/**
* @author simar bawa
*/
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class InquirySummaryView {
@Id
UUID id;
String firstName;
String initialState;
String currentState;
Integer score;
}

View File

@@ -0,0 +1,10 @@
spring.application.name=planmatchservice
logging.level.com.example.match=DEBUG
spring.datasource.url=jdbc:postgresql://localhost:5432/planmatch
spring.datasource.username=planmatch
spring.datasource.password=secret
spring.jpa.generate-ddl=true
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
axon.axonserver.servers=localhost:8124