Spring Cloud banking platform: customers, accounts, service discovery, an API gateway, a Spring AI chatbot (REST, Discord, Telegram), MCP tools, and an Angular operations console.
The Angular app talks only to the Gateway. Discord and Telegram talk to EBank Bot directly.
- Architecture
- Services
- Prerequisites
- Run the platform
- Angular frontend
- Service APIs
- EBank Bot
- Project structure
- Tech stack
- How to verify
[ Angular / Telegram / Discord ]
|
v
gateway-service (Angular only; Discord/Telegram skip Gateway)
|
v
discovery-service (Eureka)
|
+------+------+
| |
v v
customer-service ebank-service
| |
+------+------+
|
OpenFeign + Circuit Breaker (EBank → Customer)
^
|
ebank-bot (ChatClient + MCP clients → both services)
- Gateway (
9999) routes by Eureka service id:/CUSTOMER-SERVICE/**,/EBANK-SERVICE/**,/EBANK-BOT/**. - EBank Service loads customers with OpenFeign (
CUSTOMER-SERVICE) and a Resilience4j Circuit Breaker (customerService) on account list and by-id lookup. Fallback customer: nameNot Available, emailnot available. - Customer Service and EBank Service expose MCP Streamable HTTP at
/mcp. EBank Bot uses those tools through Spring AIChatClient. - OpenFeign and Circuit Breaker are implemented. Retry, RateLimiter, Bulkhead, and TimeLimiter are not used.
- Later stages: Config Server, Keycloak, Docker.
| Service | Port | Role |
|---|---|---|
discovery-service |
8761 | Eureka registry |
customer-service |
8056 | Customer REST + MCP (/mcp) |
ebank-service |
8057 | Account REST + MCP (/mcp) + Feign to customers |
ebank-bot |
8058 | Chat REST, Discord, Telegram → EBankAgent |
gateway-service |
9999 | Single HTTP entry; Eureka Discovery Locator |
angular-front |
4200 | Accounts, Customers, Bot UI via Gateway |
- JDK 21 (
JAVA_HOMEmust point to JDK 21) - Apache Maven 3.9+
- Node.js 24+ and npm (for
angular-front) OPENAI_API_KEYfor live chatbot answers (not stored in the repository)
Start order: Discovery → Customer → EBank → EBank Bot → Gateway → Angular.
From the repository root:
mvn -pl discovery-service spring-boot:run
mvn -pl customer-service spring-boot:run
mvn -pl ebank-service spring-boot:run
mvn -pl ebank-bot spring-boot:run
mvn -pl gateway-service spring-boot:runcd angular-front
npm startOpen http://localhost:4200 (redirects to /accounts).
CSS, no SSR. HttpClient uses http://localhost:9999 only. Gateway CORS allows Origin http://localhost:4200.
| Page | URL | Backend |
|---|---|---|
| Accounts | http://localhost:4200/accounts | GET /EBANK-SERVICE/accounts (includes customer name/email) |
| Customers | http://localhost:4200/customers | GET /CUSTOMER-SERVICE/customers |
| Bot | http://localhost:4200/bot | GET /EBANK-BOT/chat/stream (Ask). GET /EBANK-BOT/chat remains in code |
Use Refresh on Accounts and Customers after the bot creates data. Click an account ID to copy it for bot questions.
mvn -pl customer-service spring-boot:run| Resource | URL |
|---|---|
| List customers | GET http://localhost:8056/customers |
| Get customer by ID | GET http://localhost:8056/customers/{id} |
| Create customer | POST http://localhost:8056/customers |
| MCP Streamable HTTP | http://localhost:8056/mcp |
| Health | GET http://localhost:8056/actuator/health |
| Swagger UI | http://localhost:8056/swagger-ui.html |
| H2 console | http://localhost:8056/h2-console (JDBC URL jdbc:h2:mem:customerdb) |
MCP tools wrap the existing service methods: getAllCustomers, getCustomerById, saveCustomer. REST is unchanged.
mvn -pl ebank-service spring-boot:run| Resource | URL |
|---|---|
| List accounts | GET http://localhost:8057/accounts |
| Get account by ID | GET http://localhost:8057/accounts/{id} |
| Create account | POST http://localhost:8057/accounts |
| MCP Streamable HTTP | http://localhost:8057/mcp |
| Health | GET http://localhost:8057/actuator/health |
| Swagger UI | http://localhost:8057/swagger-ui.html |
| H2 console | http://localhost:8057/h2-console (JDBC URL jdbc:h2:mem:ebankdb) |
BankAccount.customerId is a Long. Nested customer is @Transient (not stored in H2). List and by-id load the customer through CustomerLookupService → Feign CUSTOMER-SERVICE behind the customerService circuit breaker. POST /accounts still validates the customer through Feign with no fallback.
MCP tools: getAllBankAccounts, getBankAccountById, saveBankAccount. Account type is CURRENT or SAVING. There is no by-customer query API; list and filter by customerId.
mvn -pl discovery-service spring-boot:run| Resource | URL |
|---|---|
| Eureka dashboard | http://localhost:8761 |
| Health | GET http://localhost:8761/actuator/health |
Standalone Eureka Server (register-with-eureka=false, fetch-registry=false). Customer, EBank, Gateway, and EBank Bot register as clients.
Run after Discovery (8761) is up:
mvn -pl gateway-service spring-boot:run| Resource | URL |
|---|---|
| Customers via Gateway | GET http://localhost:9999/CUSTOMER-SERVICE/customers |
| Accounts via Gateway | GET http://localhost:9999/EBANK-SERVICE/accounts |
| Chat via Gateway | GET http://localhost:9999/EBANK-BOT/chat?query=bonjour |
| Streamed chat via Gateway | GET http://localhost:9999/EBANK-BOT/chat/stream?query=bonjour |
| Health | GET http://localhost:9999/actuator/health |
Routes come from Eureka Discovery Locator. Static localhost:8056 / 8057 / 8058 routes are not used. The Gateway does not call OpenAI or MCP; it only forwards HTTP.
Requires OPENAI_API_KEY. Discord and Telegram reuse that key through EBankAgent. Run after Discovery is up:
mvn -pl ebank-bot spring-boot:run| Resource | URL |
|---|---|
| Chat | GET http://localhost:8058/chat?query=bonjour |
Chat (default query bonjour) |
GET http://localhost:8058/chat |
| Chat with conversation | GET http://localhost:8058/chat?query=My%20name%20is%20Mohammed&conversationId=1 |
| Chat via Gateway | GET http://localhost:9999/EBANK-BOT/chat?query=bonjour |
| Health | GET http://localhost:8058/actuator/health |
| Swagger UI | http://localhost:8058/swagger-ui.html |
GET /chat returns text/plain. EBankAgent uses ChatClient with MessageChatMemoryAdvisor, in-memory ChatMemory, and MCP tools for both backend services. The same conversationId keeps prior messages; omit it to use default. Discord uses discord-{channelId} and Telegram uses telegram-{chatId}. Direct :8058/chat and Gateway /EBANK-BOT/chat both work.
telegrambots 6.9.7.1 brings Jersey onto the classpath. Eureka would otherwise pick an incomplete Jersey transport, so EBank Bot sets eureka.client.jersey.enabled=false and still registers as EBANK-BOT.
Do not commit tokens. Supply them as environment variables or local overrides:
| Variable / property | Purpose |
|---|---|
OPENAI_API_KEY / spring.ai.openai.api-key |
Required for live chatbot answers |
DISCORD_TOKEN / discord.token |
Optional; REST /chat starts without it |
TELEGRAM_BOT_TOKEN / telegram.bot.token |
Optional; omitted means Telegram is not started |
TELEGRAM_BOT_USERNAME / telegram.bot.username |
Defaults to EBANK-BOT if omitted |
- Create an application and bot in the Discord Developer Portal.
- Enable the Message Content Intent on the bot.
- Copy the bot token and set
DISCORD_TOKEN(never commit it). - Invite the bot to a server with permission to read and send messages.
- Start Discovery → Customer Service → EBank Service → EBank Bot. Gateway is not required.
- Send a message such as
List all customers.
Expected path: Discord → EBankAgent → ChatClient → OpenAI → Customer MCP → Customer Service → Discord.
- Open Telegram and start a chat with BotFather.
- Send
/newbotand choose a unique display name and a username that ends withbot. - Copy the token and set
TELEGRAM_BOT_TOKEN(and optionallyTELEGRAM_BOT_USERNAME). Never commit them. - Start Discovery → Customer Service → EBank Service → EBank Bot. Gateway is not required.
- Send a message such as
List all bank accounts.
Expected path: Telegram → EBankAgent → ChatClient → OpenAI → EBank MCP → EBank Service → Telegram.
Gateway examples (Discovery, Customer, EBank, Bot, and Gateway running, plus OPENAI_API_KEY):
GET http://localhost:9999/EBANK-BOT/chat?query=bonjour
GET http://localhost:9999/EBANK-BOT/chat?query=My%20name%20is%20Mohammed&conversationId=gateway-test
GET http://localhost:9999/EBANK-BOT/chat?query=What%20is%20my%20name%3F&conversationId=gateway-test
GET http://localhost:9999/EBANK-BOT/chat?query=List%20all%20customers&conversationId=gateway-test
GET http://localhost:9999/EBANK-BOT/chat?query=List%20all%20bank%20accounts&conversationId=gateway-test
ebank-microservices/
├── customer-service/
├── ebank-service/
├── discovery-service/
├── gateway-service/
├── ebank-bot/
├── angular-front/
├── pom.xml
├── README.md
└── .gitignore
Each service is an independent Maven Spring Boot application. The root pom.xml is an aggregator only.
- Java 21
- Spring Boot 3.5.16
- Spring Cloud 2025.0.3
- Spring AI 1.1.5 (
ebank-botChatClient + MCP client;customer-serviceandebank-serviceMCP servers) - Discord:
com.zgamelogic:spring-boot-starter-discord5.0.4 - Telegram:
org.telegram:telegrambots6.9.7.1 - Angular 21 (
angular-front) - Maven
From the repository root:
mvn -q testOr one service:
mvn -q -pl customer-service testAngular:
cd angular-front
npx ng test