-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from tjschutte/main
Create Java version of Credit-Card-Product service, and switch application to use it.
- Loading branch information
Showing
63 changed files
with
1,623 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,90 @@ | ||
# Notes | ||
|
||
This repo is for the Ambar Event Sourcing / Microservices courses. | ||
You can use this repo to deploy a real world credit card application which leverages both event sourcing and microservices | ||
to create a larger application. Note that this is an academic example, implemented in multiple languages and with multiple | ||
components - you should not expect to deeply understand everything in all the languages. | ||
|
||
## Preparation Steps | ||
|
||
These steps will be performed with your instructor during the first coding session of your course, and will allow you to | ||
deploy this repo into a cloud environment running on Google Cloud Platform (GCP) | ||
|
||
1. Fork this repository | ||
2. Create two GitHub action secrets (provided by your course instructor) as follows: | ||
``` | ||
STATE_MANAGEMENT_BASE64=some_string_here | ||
CREDENTIALS_BASE64=some_string_here | ||
``` | ||
3. Make a blank commit, and push it to GitHub. | ||
**N.B. Make sure to enter the entire string for each secret, with no quotes, and no newlines!** | ||
3. Make a trivial commit (such as updating this readme), and push it to GitHub. | ||
|
||
## Extending this program | ||
|
||
Our sample application attempts to model a financial institution which offers different credit cards (products). We have | ||
already modeled some services of the application - some multiple times in multiple languages! To get started, you should | ||
look at one of the already modeled services like the Credit-Card-Product service which is implemented in multiple languages | ||
and try to follow along in one of the languages you are familiar with (php, java). Reimplement the service in your language | ||
of choice, or add any missing features to the module. | ||
|
||
Once you have an understanding of the pattern as it works in your chosen implementation language, feel free to implement | ||
another service of the application. If you feel inclined, open a pull request to the Ambar repo, and we will review and merge | ||
it and extend the frontend application to leverage the capabilities! | ||
|
||
### Event Sourcing Additional Context / Reminders | ||
|
||
A quick reminder on Event Sourcing | ||
|
||
Event Sourcing is the inversion of state in an application. Instead of directly storing state in a record and performing CRUD | ||
operations on those records, we record series of events that describe what happened in our application and derive state from | ||
one or more of those events. This gives us a direct historical event sequence that we can use to build up state in many ways | ||
and to even 'time travel' to see what state was in the past, without the need for audit tables or trying to deal with the | ||
dual write problem. | ||
|
||
Note: This does not mean we do not directly store state in event sourcing! We can still distill information from our events | ||
to build useful models about the state of our application and its data. This is where projections and read models come in. | ||
|
||
An easy way to organize event sourcing conceptually, and in code, is by leveraging the Command Query Responsibility Separation | ||
pattern (CQRS) where we create distinct paths for our write (record events) and read (leverage events / state) actions. | ||
|
||
#### Terms refresher | ||
|
||
* **Command**: A request for something to happen in our application. | ||
* E.G. Making a new credit card product for customers to apply for. | ||
* **Event**: A recording of something significant happening in our application containing things like who, what, and when. | ||
Events should be small and precise, and are written sequentially to an immutable log. Never change events or insert new ones | ||
except at the tail of the log. Events should belong to short-lived processes, such as handling a request or system notification. | ||
* E.G. Activating a credit card product so customers can apply for it | ||
(Who: The product ID, What: Make it active, When: when was the event recorded) | ||
* **Aggregate**: Information Model built from a set of related events used to model a process such has handling a command / request. | ||
They should be small, and leverage event ordering to create a minified state for process handling. | ||
* E.G. A Product Aggregate to model a credit card which is offered in our service. We can leverage the aggregate to determine | ||
if a product is already available (active) or not, to determine if a command (request) is valid. | ||
* **Projection**: Projection leverages building up some useful state from a filtered set of events and storing the derived | ||
state into a projection database. These projections can then be leveraged during command validations when determining if | ||
a command should be accepted (event written) or not. | ||
* **Query**: Queries are the read side of our application, and leverage the models created by projections to retrieve information | ||
store about the state of our application. | ||
|
||
``` | ||
Comamnd (Read from EventStore/ReadModelStore, write back to EventStore) | ||
-> [Command (Request)] -> [Build some state (EventStore Aggregate)] -> [Perform Validations (Aggregate, ReadModels)] -> | ||
[Record whatever happened (New Event)] -> [EventStore] | ||
(Strongly Consistent) ^ | ||
------------------------------------------------------------------------------------------------------------------------ | ||
(Eventually Consistent) v | ||
Reaction (Event from EventStore, write back to EventStore) | ||
-> [EventStore(Event)] -> [Build some state (EventStore Aggregate)] -> [Perform Validations (Aggregate, ReadModels)] -> | ||
[Record whatever happened (New Event)] -> [EventStore] | ||
Projections (Read from ReadModelStore, write to ReadModelStore) | ||
-> [EventStore(Event)] -> [Build some state] -> [Project some interesting state] -> [ReadModelStore] | ||
Querys (Read Commands) (Read from ReadModelStore) | ||
-> [ReadModelStore (Modeled State)] | ||
``` | ||
|
||
### Microservices Additional Context / Reminders |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
code/vendor | ||
code/development-environment |
15 changes: 15 additions & 0 deletions
15
application/java-credit-card-product-service/CreditCardProduct.iml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<module version="4"> | ||
<component name="FacetManager"> | ||
<facet type="jpa" name="JPA"> | ||
<configuration> | ||
<setting name="validation-enabled" value="true" /> | ||
<setting name="provider-name" value="Hibernate" /> | ||
<datasource-mapping> | ||
<factory-entry name="entityManagerFactory" value="e2079969-38fd-4e93-8daa-97fd5223b996" /> | ||
</datasource-mapping> | ||
<naming-strategy-map /> | ||
</configuration> | ||
</facet> | ||
</component> | ||
</module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Step 1: Use Maven image to build the application | ||
FROM maven:3.9.5-eclipse-temurin-21 AS build | ||
|
||
# Set the working directory inside the container | ||
WORKDIR /app | ||
|
||
# Copy the pom.xml and download dependencies (layer caching) | ||
COPY pom.xml . | ||
RUN mvn dependency:go-offline | ||
|
||
# Copy the rest of the application source code | ||
COPY src ./src | ||
|
||
# Build the Spring Boot application | ||
RUN mvn clean package -DskipTests | ||
|
||
# Step 2: Use JDK 21 runtime image to run the application | ||
FROM eclipse-temurin:21-jdk | ||
|
||
# Set the working directory for the runtime container | ||
WORKDIR /app | ||
|
||
# Copy the JAR file from the build image to the runtime image | ||
COPY --from=build /app/target/*.jar app.jar | ||
|
||
# Expose port 8080 (Spring Boot default port) | ||
EXPOSE 8080 | ||
|
||
# Set the entry point to run the application | ||
ENTRYPOINT ["java", "-jar", "app.jar"] |
1 change: 1 addition & 0 deletions
1
application/java-credit-card-product-service/development-environment/.gitignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
data |
41 changes: 41 additions & 0 deletions
41
application/java-credit-card-product-service/development-environment/dev-commands.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
endpoint="localhost:8080" | ||
|
||
# To create the Starter card | ||
curl -X POST "${endpoint}/api/v1/credit_card_product/product" \ | ||
-H "Content-Type: application/json" \ | ||
-d '{ | ||
"productIdentifierForAggregateIdHash": "STARTER_CREDIT_CARD", | ||
"name": "Starter", | ||
"interestInBasisPoints": 1200, | ||
"annualFeeInCents": 5000, | ||
"paymentCycle": "monthly", | ||
"creditLimitInCents": 50000, | ||
"maxBalanceTransferAllowedInCents": 0, | ||
"reward": "none", | ||
"cardBackgroundHex": "#7fffd4" | ||
}' | ||
|
||
# To create the Platinum card | ||
curl -X POST "${endpoint}/api/v1/credit_card_product/product" \ | ||
-H "Content-Type: application/json" \ | ||
-d '{ | ||
"productIdentifierForAggregateIdHash": "PLATINUM_CREDIT_CARD", | ||
"name": "Platinum", | ||
"interestInBasisPoints": 300, | ||
"annualFeeInCents": 50000, | ||
"paymentCycle": "monthly", | ||
"creditLimitInCents": 500000, | ||
"maxBalanceTransferAllowedInCents": 100000, | ||
"reward": "points", | ||
"cardBackgroundHex": "#E5E4E2" | ||
}' | ||
|
||
# To list the current card products | ||
curl -X POST "${endpoint}/api/v1/credit_card_product/product/list-items" | jq . | ||
|
||
productId="" | ||
# Activate a product | ||
curl -X POST "${endpoint}/api/v1/credit_card_product/product/activate/${productId}" | ||
|
||
# Deactivate a product | ||
curl -X POST "${endpoint}/api/v1/credit_card_product/product/deactivate/${productId}" |
7 changes: 7 additions & 0 deletions
7
application/java-credit-card-product-service/development-environment/dev_a_start.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#!/bin/bash | ||
set -e | ||
|
||
#docker run --rm -v $(pwd):/app composer:2.2.24 install --ignore-platform-reqs | ||
docker compose down | ||
docker compose up -d --build --force-recreate | ||
sleep 5 |
4 changes: 4 additions & 0 deletions
4
application/java-credit-card-product-service/development-environment/dev_z_shutdown.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/bin/bash | ||
set -e | ||
|
||
docker compose down |
84 changes: 84 additions & 0 deletions
84
application/java-credit-card-product-service/development-environment/docker-compose.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
services: | ||
|
||
java-credit-card-product: | ||
container_name: java-credit-card-product | ||
build: | ||
context: ./../ | ||
volumes: | ||
- ./../:/srv | ||
restart: always | ||
environment: | ||
EVENT_STORE_HOST: "172.30.0.102" | ||
EVENT_STORE_PORT: 5432 | ||
EVENT_STORE_DATABASE_NAME: "my_es_database" | ||
EVENT_STORE_USER: "my_es_username" | ||
EVENT_STORE_PASSWORD: "my_es_password" | ||
EVENT_STORE_CREATE_TABLE_WITH_NAME: "event_store" | ||
EVENT_STORE_CREATE_REPLICATION_USER_WITH_USERNAME: "replication_username_test" | ||
EVENT_STORE_CREATE_REPLICATION_USER_WITH_PASSWORD: "replication_password_test" | ||
EVENT_STORE_CREATE_REPLICATION_PUBLICATION: "replication_publication_test" | ||
MONGODB_PROJECTION_HOST: "172.30.0.103" | ||
MONGODB_PROJECTION_PORT: 27017 | ||
MONGODB_PROJECTION_AUTHENTICATION_DATABASE: "admin" | ||
MONGODB_PROJECTION_DATABASE_NAME: "projections" | ||
MONGODB_PROJECTION_DATABASE_USERNAME: "my_mongo_username" | ||
MONGODB_PROJECTION_DATABASE_PASSWORD: "my_mongo_password" | ||
SESSION_TOKENS_EXPIRE_AFTER_SECONDS: 3600 | ||
depends_on: | ||
- pg-event-store | ||
- mongo-projection-reaction | ||
networks: | ||
TestNetwork: | ||
ipv4_address: 172.30.0.101 | ||
deploy: | ||
resources: | ||
limits: | ||
cpus: '0.500' | ||
memory: 1024M | ||
|
||
pg-event-store: | ||
build: | ||
context: | ||
./pg-event-store | ||
container_name: pg-event-store | ||
restart: always | ||
volumes: | ||
- ./data/event-store/pg-data:/var/lib/postgresql/data | ||
environment: | ||
POSTGRES_USER: my_es_username | ||
POSTGRES_DB: my_es_database | ||
POSTGRES_PASSWORD: my_es_password | ||
command: > | ||
postgres -c wal_level=logical | ||
-c ssl=on | ||
-c ssl_cert_file=/var/lib/postgresql/certs/server.crt | ||
-c ssl_key_file=/var/lib/postgresql/certs/server.key | ||
expose: | ||
- 5432 | ||
networks: | ||
TestNetwork: | ||
ipv4_address: 172.30.0.102 | ||
|
||
mongo-projection-reaction: | ||
build: | ||
context: | ||
./mongo-projection-reaction | ||
container_name: mongo-projection-reaction | ||
restart: always | ||
environment: | ||
MONGO_INITDB_ROOT_USERNAME: my_mongo_username | ||
MONGO_INITDB_ROOT_PASSWORD: my_mongo_password | ||
volumes: | ||
- ./data/mongo-projection-reaction/db-data:/data/db | ||
expose: | ||
- 27017 | ||
networks: | ||
TestNetwork: | ||
ipv4_address: 172.30.0.103 | ||
|
||
networks: | ||
TestNetwork: | ||
driver: bridge | ||
ipam: | ||
config: | ||
- subnet: 172.30.0.0/24 |
1 change: 1 addition & 0 deletions
1
...-credit-card-product-service/development-environment/mongo-projection-reaction/Dockerfile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
FROM mongo:7.0.14 |
9 changes: 9 additions & 0 deletions
9
...cation/java-credit-card-product-service/development-environment/pg-event-store/Dockerfile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
FROM postgres:16.4 | ||
|
||
RUN mkdir -p /var/lib/postgresql/certs | ||
RUN openssl genrsa -out /var/lib/postgresql/certs/server.key 2048 | ||
RUN chmod 600 /var/lib/postgresql/certs/server.key | ||
RUN openssl req -new -key /var/lib/postgresql/certs/server.key -out /var/lib/postgresql/certs/server.csr -subj "/C=GB/ST=London/L=London/O=SnakeOil/OU=Org/CN=snake-oil.oil" | ||
RUN openssl x509 -req -in /var/lib/postgresql/certs/server.csr -signkey /var/lib/postgresql/certs/server.key -out /var/lib/postgresql/certs/server.crt -days 36500 | ||
RUN chown 999 /var/lib/postgresql/certs | ||
RUN chown 999 /var/lib/postgresql/certs -Rf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?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>3.3.4</version> | ||
<relativePath/> <!-- lookup parent from repository --> | ||
</parent> | ||
<groupId>cloud.ambar</groupId> | ||
<artifactId>CreditCardProduct</artifactId> | ||
<version>0.0.1-SNAPSHOT</version> | ||
<name>CreditCardProduct</name> | ||
<description>Ambar Credit Card Product service for course demo</description> | ||
<url/> | ||
<licenses> | ||
<license/> | ||
</licenses> | ||
<developers> | ||
<developer/> | ||
</developers> | ||
<scm> | ||
<connection/> | ||
<developerConnection/> | ||
<tag/> | ||
<url/> | ||
</scm> | ||
<properties> | ||
<java.version>21</java.version> | ||
</properties> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter</artifactId> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
</dependency> | ||
|
||
<!-- Java Persistence API --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-data-jpa</artifactId> | ||
</dependency> | ||
|
||
<!-- Database Drivers --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-data-mongodb</artifactId> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.postgresql</groupId> | ||
<artifactId>postgresql</artifactId> | ||
</dependency> | ||
|
||
<!-- Lombok for reducing boilerplate code --> | ||
<dependency> | ||
<groupId>org.projectlombok</groupId> | ||
<artifactId>lombok</artifactId> | ||
<version>1.18.30</version> | ||
<scope>compile</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
<version>${project.parent.version}</version> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
</project> |
Oops, something went wrong.