DEV Community

Bladimir
Bladimir

Posted on • Originally published at bladbaez.wordpress.com

Building a Secure Demo Banking App [Part 2]: Implementing the First Microservices

REVIEW FROM PART I

In part I, I outlined the scope, architecture and overall project structure and some of the initial features for the DemoBanking App.

Initially, while exploring the API Gateway layer, I considered AWS API Gateway to be integrated due to its convenient features. However, after hitting some roadblocks regarding the AWS costs and maintenance for a demo project I decided to go for Spring Gateway. Even though AWS offers a generous 12-month free tier, I’d already used mine for other hands-on labs and experimental projects.

From the bright side, choosing Spring Gateway makes everything inside Spring ecosystem which eliminates extra configurations for making things compatible between two vendors tools. Also, it allows anyone that clones the project repo to run the app without this limitation.

DemoBank app logo

TECHNOLOGY STACK:

Frontend: React, TypeScript, Tailwind CSS.
Backend: Spring Framework (Boot, Data, Gateway)
Database: PostgreSQL (for user, account and transaction microservices)
Authentication: OAuth2 / OpenID Connect
Observability: Apache Kafka, Prometheus, Grafana, OpenTelemetry, ElasticSearch

Project Structure (so far)

  • demo-banking-app (project root)
  • /docs
  • /observability
  • /tests
  • /docker-compose.yml
  • README.md
  • pom.xml
  • /services (detailed below)

Inside /services directory, the user, account and transaction microservices follow a similar structure:

  • /controller: it handles the relevant API endpoints.
  • /dto: as the acronym suggests, it is for managing “data transfer objects” without touching the JPA entities directly.
  • /entity: it represents the entity class for each microservice.
  • /repository: responsible for interacting or communicating with the database source using the Spring JpaRepository.
  • /service: this is where all the business logic is handled.
  • /exceptions: this will contain the classes for handling exceptions (pending to be implemented).
  • /utils: it includes any utility or common classes such as mappers to “map” from an entity to DTO, and viceversa.

MICROSERVICES

As I mentioned before, there are three microservices implemented at this stage so far.

Microservice #1: User

It is considered to be one of the core microservices of the project since it is responsible to manage everything related to users.

Key endpoints:

8081/users
GET 8081/users/all
GET 8081/users/{id}
POST 8081/users/create
PATCH 8081/users/update/{id}
PATCH 8081/users/disable

Microservice #2: Account
Similarly, the account microservice handles every aspect related to bank accounts in this demo project.

Key endpoints:

8082/accounts
GET 8082/accounts/all
POST 8082/accounts/create
GET 8082/accounts/{accountNumber}
PATCH 8082/accounts/update/{accountNumber}
PATCH 8082/accounts/inactivate/{accountNumber}

Microservice #3: Transaction
Finally, there is the transaction microservice which handles the bank transactions.

Key endpoints:

8083/transactions
GET 8083/transactions/all
POST 8083/transactions/create/{type}
GET 8083/transactions/{id}
PATCH 8083/transactions/cancel/{id}

DOCKER SETUP: RUNNING LOCALLY WITH DOCKER COMPOSE
In order to make the project easy to run without manual configuration difficulties, the initial version uses a single docker-compose.yml file at the root. This will handle:

  • The Spring Cloud Gateway (entry point, which is pending to be implemented)
  • The three implemented microservices (User, Account, Transaction)
  • A PostgreSQL instance (with separate schemas or databases per service)

Each microservice has its own Dockerfile located in the root folder, using a multi-stage Maven build for efficiency:

1. Build stage: Compiles the Spring Boot JAR with dependencies.
2. Runtime stage: Copies the slim JAR into a lightweight OpenJDK/JRE image.

Quick start commands (which is also included in the README):

Clone
git clone https://github.com/blad90/demo-banking-app.git

Build and start all services + DB
docker compose up -d --build

Check logs for one service (e.g., user-service)
docker compose logs -f user-service

Stop everything
docker compose down

CONSIDERATIONS AND REFINEMENTS

Saga Pattern (orchestration vs. choreography):

It is important to mention that initially, since the Saga Pattern was the design choice for the software architecture I analyzed which would be more appropriate for the project between the orchestration and choreography approach. While exploring about the two regarding the advantages and disadvantages according to the project scope, I concluded the Saga Orchestration is the right selection for the following reasons:

  • It uses a main orchestrator, where it centralizes all the communications between microservices.
  • For the level of complexity that will take this demo-banking application as it evolves, it facilitates the debugging process when anything goes wrong.
  • The choreography approach may not fit in this project well since communication happens among microservices themselves, which could be a headache when the application grows in complexity.

Relationships between entity models

When completing the initial versions of each microservice (User, Account and Transaction), I started to wonder about how could be appropriate to handle the relationships among entities. For example, when Account requires to be linked to an User.

Based on previous experiences in developing monolithic applications, for instance the ones based on Spring, I know that it is straightforward establishing relationships using JPA annotations to represent entity cardinalities such as @OneToMany, @OneToOne, and so on. However, when building more complex applications, specially those with an architecture based on microservices, I started to notice that it wasn’t the right approach because of the following:

  • If two or more entities from distinct microservices references to each other, it produces a tight coupling issue.
  • It is hard to maintain in the long run if entities or components tightly coupled.
  • Tight coupling affects the microservices autonomy.

As a result, while exploring and researching alternatives, the event-driven cache updates pattern came into play, which is great for performance and resilience in complex, distributed systems such as banking applications. This pattern allows data sharing between services through events keeping microservices independence. Each service keeps a cache version of the data received from another service. For instance, Account microservice stores User data on its own database schema, which is a cache version received from the User microservice, without impacting its database.

Below is the updated architecture diagram, mostly about the switch between AWS API Gateway to Spring Gateway mentioned in the “Review from Part I” section:

Original version

Project Architecture first version

Updated version

Project Architecture second version

WHAT’S NEXT?

I’ll prioritize to complete the pending tasks and then add new features based on the actual architecture. It includes:

  • Exception handling and validation for each of the implemented microservice in this stage.
  • Implement the API Gateway.
  • Apply the event-driven cache updates pattern for communication among relevant services, using Apache Kafka.

For more

Follow along on X [@bladbaez], I’m open to feedback, suggestions or anything else.

Top comments (1)

Collapse
 
jadder profile image
Jadder Moya

this article is really interesting!