Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Gianluca-X/DigitalMoney/llms.txt

Use this file to discover all available pages before exploring further.

Digital Money House is built on a microservices architecture where every concern — authentication, user management, accounts and transactions — lives in its own independently deployable Spring Boot service. Services discover each other through a central Eureka registry, communicate synchronously via Feign clients, and exchange asynchronous events over RabbitMQ, while all external traffic enters through a single reactive API Gateway.

System Topology

                         ┌─────────────────────────────┐
                         │       Frontend (React)        │
                         │  digital-money-front-end.app  │
                         └──────────────┬──────────────┘
                                        │ HTTPS

                         ┌─────────────────────────────┐
                         │         API Gateway           │
                         │         port 8085             │
                         │  JWT validation • routing     │
                         └─────┬───────────┬────────────┘
                               │           │           │
               /auth/**        │  /users/**│  /accounts/**
                               ▼           ▼           ▼
                    ┌──────────────┐  ┌──────────┐  ┌─────────────────┐
                    │ Auth Service │  │  User    │  │ Accounts Service │
                    │  port 8082   │  │ Service  │  │   port 8084      │
                    │              │  │ port 8087│  │                  │
                    └──────┬───────┘  └────┬─────┘  └────────┬────────┘
                           │               │ Feign             │ Feign
                           │               └──────────────────►│
                           │  RabbitMQ                        │
                           │  user.exchange                   │
                           └──────────────────────────────────►

                    ┌─────────────────────────────────────────┴──────┐
                    │                    MySQL                         │
                    │  auth_service_db │ user_service_db │ account_db  │
                    └─────────────────────────────────────────────────┘

           ┌──────────────────────┐      ┌──────────────────────┐
           │    Eureka Server     │      │    Config Server      │
           │      port 8761       │      │      port 8888        │
           │  Service Registry    │      │  Centralised Config   │
           └──────────────────────┘      └──────────────────────┘

Service Inventory

ServicePortResponsibilityDatabase
Auth Service8082JWT issuance, login, logout, email verification, credential updatesauth_service_db
User Service8087User registration, profile CRUD, account provisioning via Feignuser_service_db
Accounts Service8084Digital accounts (CVU/alias), card management, transfers, activity history, PDF receiptsaccount_service_db
API Gateway8085Centralized request routing, JWT validation, CORS
Eureka Server8761Service registry and health dashboard
Config Server8888Centralised Spring Cloud Config distribution

API Gateway Routing

The Spring Cloud Gateway is the only network entry point exposed to clients. Routes are defined in gateway/src/main/resources/application.yml and use load-balanced URIs (lb://) so the gateway resolves target instances dynamically from the Eureka registry:
spring:
  cloud:
    gateway:
      routes:
        - id: auth-service
          uri: lb://auth-service
          predicates:
            - Path=/auth/**

        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/users/**

        - id: accounts-service
          uri: lb://accounts-service
          predicates:
            - Path=/accounts/**
CORS is configured at the gateway level, allowing requests from the React frontend (http://localhost:3000) with GET, POST, PUT, PATCH, DELETE, and OPTIONS methods:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "http://localhost:3000"
            allowedMethods: [GET, POST, PUT, PATCH, DELETE, OPTIONS]
            allowedHeaders: [Origin, X-Requested-With, Content-Type, Accept, Authorization]
            allowCredentials: true
            maxAge: 3600
JWT validation is performed at the gateway using the spring.security.oauth2.resourceserver.jwt.secret value configured in gateway/application.yml. The accounts-service and auth-service share the JWT_SECRET environment variable; the gateway uses a separately configured secret in its own application.yml.

Service Discovery with Eureka

Every application service (Auth, User, Accounts, Config, Gateway) registers itself with the Eureka Server at startup. The registration names map directly to the spring.application.name property in each service’s configuration:
Application NameRegistered As
Auth Serviceauth-service
User Serviceuser-service
Accounts Serviceaccounts-service
Config Serverconfig-server
Gatewaygateway-service
Services resolve each other using these logical names — for example the gateway’s lb://auth-service URI tells the load-balancer ribbon to look up the auth-service registration in Eureka and forward the request to a healthy instance.
# accounts-service/application.yml — example registration config
eureka:
  instance:
    hostname: localhost
    instance-id: ${spring.application.name}:${random.value}
    statusPageUrlPath: /actuator/info
    healthCheckUrlPath: /actuator/health
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
The Eureka dashboard is accessible at http://localhost:8761 when the stack is running.

Async Messaging with RabbitMQ

Email address changes must be reflected in both the Auth Service (credential store) and the User Service (profile store). Rather than a second synchronous Feign call that would tightly couple the two services, Digital Money House uses RabbitMQ for this propagation: Flow:
  1. A client sends a PATCH /auth/change-email?newEmail={email} request with a valid Bearer token.
  2. Auth Service updates the email in its own auth_service_db record.
  3. Auth Service publishes a UserEmailChangedEvent to the user.exchange topic exchange with the routing key user.email.changed.
  4. User Service consumes the event from the user.email.changed queue and updates its local copy of the email in user_service_db.
// auth-service — UserEmailChangedEvent DTO
public class UserEmailChangedEvent {
    private Long   userId;
    private String newEmail;
    // constructors, getters, setters …
}
RabbitMQ configuration in user-service/application.properties:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

spring.rabbitmq.queues[0].name=user.email.changed
spring.rabbitmq.exchanges[0].name=user.exchange
spring.rabbitmq.exchanges[0].type=topic
spring.rabbitmq.bindings[0].exchange=user.exchange
spring.rabbitmq.bindings[0].queue=user.email.changed
spring.rabbitmq.bindings[0].routing-key=user.email.changed
The RabbitMQ management UI runs at http://localhost:15672 (credentials: guest / guest). Use it to inspect queues, exchanges, and message rates during development.

Database Isolation

Each service owns an independent MySQL schema. Schemas and their dedicated users are created by init.sql, which MySQL executes automatically on first boot via the Docker Compose init-scripts mechanism:
-- init.sql (executed once on MySQL container first start)
CREATE DATABASE IF NOT EXISTS user_service_db;
CREATE DATABASE IF NOT EXISTS account_service_db;
CREATE DATABASE IF NOT EXISTS auth_service_db;

CREATE USER IF NOT EXISTS 'user_service_user'@'%'    IDENTIFIED BY 'nerea';
GRANT ALL PRIVILEGES ON user_service_db.*    TO 'user_service_user'@'%';

CREATE USER IF NOT EXISTS 'account_service_user'@'%' IDENTIFIED BY 'nerea';
GRANT ALL PRIVILEGES ON account_service_db.* TO 'account_service_user'@'%';

CREATE USER IF NOT EXISTS 'auth_service_user'@'%'    IDENTIFIED BY 'nerea';
GRANT ALL PRIVILEGES ON auth_service_db.*    TO 'auth_service_user'@'%';
No service can directly query another service’s schema. All cross-service data access must go through the owning service’s REST API or an async event. This boundary is enforced by the per-service database users having GRANT only on their own schema.
Hibernate manages DDL in development mode (ddl-auto: update), so schema migrations are applied automatically when a service restarts.

Inter-Service Communication (Feign Clients)

Where synchronous data is required between services, Digital Money House uses Spring Cloud OpenFeign declarative HTTP clients.

User Service → Accounts Service

When a user registers, User Service calls Accounts Service to provision a new digital account. The call is authenticated with an internal token header:
@FeignClient(name = "accounts-service", configuration = FeignClientConfig.class)
public interface AccountClient {

    @PostMapping("/accounts/create")
    AccountResponse createAccount(@RequestBody AccountCreationRequest request);

    @DeleteMapping("/accounts/{accountId}")
    AccountResponse deleteAccount(@PathVariable Long accountId);
}

User Service → Auth Service

User Service calls Auth Service directly (bypassing the registry for this internal path) to register or update credentials in the auth store:
@FeignClient(name = "auth-service", url = "http://localhost:8082")
public interface AuthClient {

    @PostMapping("/auth/register")
    AuthResponse registerUser(@RequestBody UserRegisterAuthRequest request);

    @PutMapping("/auth/update")
    AuthResponse updateUserAuth(@RequestBody UserUpdateRequest request);

    @DeleteMapping("/auth/delete/{authId}")
    String deleteUserAuth(@PathVariable Long authId);
}
The AccountClient uses the Eureka-registered name accounts-service with a custom FeignClientConfig that injects the internal bearer token. The AuthClient currently uses a hardcoded URL; this is a known improvement area for moving to a fully registry-resolved URI.

Concurrency & Data Integrity

The Accounts Service protects account balances from race conditions using a pessimistic write lock on the account row during transfer processing:
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT a FROM Account a WHERE a.id = :id")
Optional<Account> findByIdForUpdate(Long id);
Combined with Spring’s @Transactional wrapping the entire transfer operation, this guarantees that concurrent transfer requests for the same account are serialised at the database level, preventing duplicate debits or phantom credits.

Build docs developers (and LLMs) love