From 4b1bf0962d76780b08f22b4b99a6494e21993f71 Mon Sep 17 00:00:00 2001
From: sbawa <45673906+sdbawa@users.noreply.github.com>
Date: Mon, 23 Sep 2019 14:01:55 -0700
Subject: [PATCH] Add files via upload
initial commit
---
Readme.md | 66 +++++++++++++++++++
pom.xml | 52 +++++++++++++++
.../com/example/match/PlanMatchService.java | 13 ++++
.../match/api/InquiryCreateCommand.java | 20 ++++++
.../match/api/InquiryCreatedEvent.java | 18 +++++
.../match/api/InquiryScoreCommand.java | 20 ++++++
.../example/match/api/InquiryScoredEvent.java | 18 +++++
.../match/api/InquirySummaryQuery.java | 15 +++++
.../example/match/client/DomainService.java | 43 ++++++++++++
.../com/example/match/command/Inquiry.java | 63 ++++++++++++++++++
.../match/query/InquirySummaryProjector.java | 45 +++++++++++++
.../match/query/InquirySummaryView.java | 27 ++++++++
src/main/resources/application.properties | 10 +++
13 files changed, 410 insertions(+)
create mode 100644 Readme.md
create mode 100644 pom.xml
create mode 100644 src/main/java/com/example/match/PlanMatchService.java
create mode 100644 src/main/java/com/example/match/api/InquiryCreateCommand.java
create mode 100644 src/main/java/com/example/match/api/InquiryCreatedEvent.java
create mode 100644 src/main/java/com/example/match/api/InquiryScoreCommand.java
create mode 100644 src/main/java/com/example/match/api/InquiryScoredEvent.java
create mode 100644 src/main/java/com/example/match/api/InquirySummaryQuery.java
create mode 100644 src/main/java/com/example/match/client/DomainService.java
create mode 100644 src/main/java/com/example/match/command/Inquiry.java
create mode 100644 src/main/java/com/example/match/query/InquirySummaryProjector.java
create mode 100644 src/main/java/com/example/match/query/InquirySummaryView.java
create mode 100644 src/main/resources/application.properties
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..b6b2ff9
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,66 @@
+This projects helps to build and deploy a microservice by applying
+DDD, CQRS, Event Souring principles on AXON JVM stack.
+
+
+
What you'll build
+You'll build a sample microservice with one bounded context.
+
+ What you'll need
+AXON stack - framework and server
+Java
+SpringBoot
+Postgres
+
+Learn more about AXON stack
+AXON is a open source stack to develop java based microservices . https://axoniq.io
+
+
+package structure
+This stack requires you to architect/structure your code is a particular way.
+
+
+api
+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
+
+
+command
+This package contains model which serves the command side of application.
+These model serves the commands only.
+
+
+query
+This package contains model which serves the query side of application.
+These model serves the queries only.
+
+client
+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.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..01229c3
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.7.RELEASE
+
+
+ com.example
+ planmatchservice
+ 1.0
+ leadgen
+ Demo AXON based microservice for LeadGen using DDD, CQRS, EventSourcing
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.axonframework
+ axon-spring-boot-starter
+ 4.1
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/src/main/java/com/example/match/PlanMatchService.java b/src/main/java/com/example/match/PlanMatchService.java
new file mode 100644
index 0000000..6f118bc
--- /dev/null
+++ b/src/main/java/com/example/match/PlanMatchService.java
@@ -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);
+ }
+
+}
diff --git a/src/main/java/com/example/match/api/InquiryCreateCommand.java b/src/main/java/com/example/match/api/InquiryCreateCommand.java
new file mode 100644
index 0000000..4d9b67f
--- /dev/null
+++ b/src/main/java/com/example/match/api/InquiryCreateCommand.java
@@ -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;
+}
diff --git a/src/main/java/com/example/match/api/InquiryCreatedEvent.java b/src/main/java/com/example/match/api/InquiryCreatedEvent.java
new file mode 100644
index 0000000..c565444
--- /dev/null
+++ b/src/main/java/com/example/match/api/InquiryCreatedEvent.java
@@ -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;
+}
diff --git a/src/main/java/com/example/match/api/InquiryScoreCommand.java b/src/main/java/com/example/match/api/InquiryScoreCommand.java
new file mode 100644
index 0000000..ab8c712
--- /dev/null
+++ b/src/main/java/com/example/match/api/InquiryScoreCommand.java
@@ -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;
+}
diff --git a/src/main/java/com/example/match/api/InquiryScoredEvent.java b/src/main/java/com/example/match/api/InquiryScoredEvent.java
new file mode 100644
index 0000000..9c859d9
--- /dev/null
+++ b/src/main/java/com/example/match/api/InquiryScoredEvent.java
@@ -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;
+}
diff --git a/src/main/java/com/example/match/api/InquirySummaryQuery.java b/src/main/java/com/example/match/api/InquirySummaryQuery.java
new file mode 100644
index 0000000..c72a5ea
--- /dev/null
+++ b/src/main/java/com/example/match/api/InquirySummaryQuery.java
@@ -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;
+}
diff --git a/src/main/java/com/example/match/client/DomainService.java b/src/main/java/com/example/match/client/DomainService.java
new file mode 100644
index 0000000..9583ab5
--- /dev/null
+++ b/src/main/java/com/example/match/client/DomainService.java
@@ -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);
+
+ }
+}
diff --git a/src/main/java/com/example/match/command/Inquiry.java b/src/main/java/com/example/match/command/Inquiry.java
new file mode 100644
index 0000000..b01d8d9
--- /dev/null
+++ b/src/main/java/com/example/match/command/Inquiry.java
@@ -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();
+ }
+}
diff --git a/src/main/java/com/example/match/query/InquirySummaryProjector.java b/src/main/java/com/example/match/query/InquirySummaryProjector.java
new file mode 100644
index 0000000..2fa427e
--- /dev/null
+++ b/src/main/java/com/example/match/query/InquirySummaryProjector.java
@@ -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());
+ }
+
+}
diff --git a/src/main/java/com/example/match/query/InquirySummaryView.java b/src/main/java/com/example/match/query/InquirySummaryView.java
new file mode 100644
index 0000000..c768d75
--- /dev/null
+++ b/src/main/java/com/example/match/query/InquirySummaryView.java
@@ -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;
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..895f3b7
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -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