ACME Mobility is the project for the Microservices Software Architectures, Academic Year 2025/2026 course. It implements a short-term urban rental platform for electric vehicles—cars, scooters, and kick scooters—distributed across city stations.
The repository includes both the executable system and the analysis and design artifacts required by the project specification: global choreography, participant projections, documentary and executable BPMN diagrams, and UML models using the TinySOA profile.
This is the project's only README. Run Docker commands from the `acme-mobility` directory.
- Application features
- How it works
- Architecture
- Repository structure
- Starting with Docker Compose
- Ports and endpoints
- Initial data
- Development and testing
- Project artifacts
- Status against the specification
- User registration with name, email address, and password.
- Automatic creation of a Jolie bank account associated with the user, with a UUID identifier and an initial balance of €100.
- Login and logout through HTTP sessions and cookies.
- Session recovery when the page is refreshed.
- Display of total balance, reserved amount, and available balance.
Registration involves two services: the frontend first creates the user in the Rental Service, then creates the account through the dedicated Jolie REST service.
- Leaflet/OpenStreetMap map of the seven configured stations in Bologna.
- Display of the vehicles at each station.
- Distinction between cars, scooters, and kick scooters.
- Status and battery-level indicators.
- Managed statuses: `AVAILABLE`, `BOOKED`, `RENTED`, `MAINTENANCE`, and `CHARGING`.
- Access to instant rental or reservation selection from the map.
The user selects or enters the vehicle ID, which represents a QR-code scan. If the vehicle is available and the user does not already have a reservation:
- Camunda starts the instant-rental branch.
- The Bank Service reserves a €10 deposit.
- Fleet Management starts tracking and battery monitoring.
- The Stations Service simulates the physical unlock.
- The vehicle changes to `RENTED`.
- The UI opens the active ride.
The project does not use a camera: QR scanning is simulated through the vehicle identifier.
The user selects a station and a vehicle category. The Rental Service assigns the `AVAILABLE` vehicle of that type with the highest battery level, reserves €10 on the account, and changes its status to `BOOKED`.
The reservation:
- lasts 30 minutes;
- shows a countdown;
- can be collected by scanning the assigned vehicle;
- can be cancelled free of charge while at least 5 minutes remain;
- converts the deposit into a charge when cancelled later;
- also converts the deposit into a charge when it expires uncollected.
A user can have only one active reservation. A reserved vehicle can be collected only by the user to whom it was assigned.
During a ride, Fleet Management:
- chooses a destination station different from the departure station;
- calculates a route with GraphHopper, using linear interpolation as a fallback;
- simulates 20 vehicle movements after an initial 15-second delay;
- updates position and battery every 3 seconds;
- sends data to the Tracking Service and Battery Service through gRPC.
The Rental Service periodically queries the Fleet Management gateway and publishes updates to the browser through Redis Pub/Sub and Server-Sent Events. The ride page displays the current map position, departure and destination stations, battery level, duration, connection state, and arrival at the destination. The UI enables ride completion when the simulation reports arrival.
When ride completion is requested, Camunda coordinates:
- Reading the latest telemetry.
- Physically locking the vehicle at the arrival station.
- Stopping tracking and battery monitoring.
- Calculating and charging the fare.
- Releasing the reserved deposit.
- Persisting the completed rental.
- Sending the summary to the frontend.
Implemented rates are:
| Vehicle | Fixed fee | Rate per minute |
|---|---|---|
| Car | €2.00 | €0.25 |
| Scooter | €1.50 | €0.20 |
| Kick scooter | €1.00 | €0.15 |
To make payment observable during a demo, one real second is equivalent to 15 billed seconds. The total is rounded to cents; a 10% penalty is added when the final battery level is below 15%.
The summary shows the vehicle, destination, duration, final battery level, price, and any penalty.
After the summary, the user can finish without reporting damage or select damaged parts from a vehicle-type-specific list and submit the report.
If no damage is reported:
- with a battery level of at least 15%, the vehicle returns to `AVAILABLE`;
- with a battery level below 15%, it changes to `CHARGING`; the Stations Service simulates charging up to 100% and notifies the Rental Service, which returns it to `AVAILABLE`.
If damage is reported, the vehicle changes to `MAINTENANCE` and is added to the Maintenance Service queue. The simulated duration is:
```text 1 minute × (1 + number of damages) ```
The Maintenance Service keeps an in-memory FIFO queue, recovers vehicles already under maintenance from the Rental Service on startup, and processes the queue every day at 09:00 in the `Europe/Rome` time zone. On completion, it sets the battery to 100% and requests that the vehicle return to `AVAILABLE`.
A separate BPMN process, started every day at midnight, also finds available or already-maintained vehicles whose last maintenance was at least 30 days earlier, marks them as `MAINTENANCE`, and queues them.
The Rental Service is the application entry point and owns the persistent state of users, process sessions, stations, vehicles, reservations, and rentals. It does not contain the full flow in a Java sequence: it publishes messages to Camunda and provides job workers that implement BPMN process tasks.
Camunda stores workflow state and coordinates calls to banking, stations, Fleet Management, and maintenance. For each authenticated user, the Rental Service associates a `ProcessSession` with the current Camunda instance.
The frontend does not decide autonomously which stage a rental is in:
```text BPMN state → Rental Service policy → GET /api/rentals/resume → Angular guard → canonical route ```
This ensures that refreshes, manually entered URLs, and asynchronous updates return the user to the page consistent with the actual process state. Reservation, scanning, cancellation, ride-completion, and reporting commands are also authorized on the backend according to the BPMN stage.
Redis transports process notifications and balance changes. SSE events speed up UI refreshes, while `/resume` remains the authoritative source for navigation.
```mermaid sequenceDiagram actor U as User / Angular participant R as Rental Service participant C as Camunda participant B as Bank Jolie participant S as Stations Service participant F as Fleet Management
U->>R: login and open map
R->>C: create/resume user instance
U->>R: scan QR or reserve
R->>C: correlate command
C->>B: reserve €10 via SOAP
C->>F: start monitoring via REST
F->>F: tracking + battery via gRPC
C->>S: unlock vehicle via REST
loop simulated ride
C->>F: request telemetry
C-->>U: position and battery via SSE
end
U->>R: end ride
C->>S: lock vehicle
C->>F: stop monitoring
C->>B: charge fare and release reservation
C-->>U: summary
U->>R: no damage or report
```
```mermaid
flowchart LR
UI["Angular Frontend
:4200"] -->|REST + SSE| Rental["Rental Service
:8080"]
UI -->|account-registration REST| Registration["Account Registration
Jolie :8082"]
Rental <-->|gRPC / REST| Camunda["Camunda 8 + Zeebe<br/>:8083 / :26500"]
Rental -->|SOAP| Bank["Bank Service<br/>Jolie :8000"]
Rental -->|REST| Stations["Stations Service<br/>:8084"]
Rental -->|REST| Maintenance["Maintenance Service<br/>:8085"]
Rental -->|REST| Gateway["Fleet Gateway<br/>:8091"]
Rental <--> Redis["Redis<br/>:6379"]
Rental --> RentalDB[("Rental PostgreSQL<br/>:5432")]
Bank --> BankDB[("Bank PostgreSQL<br/>:5433")]
Registration --> BankDB
Gateway -->|gRPC| Tracking["Tracking Service<br/>:8092"]
Gateway -->|gRPC| Battery["Battery Service<br/>:8093"]
```
| Component | Technology | Responsibility |
|---|---|---|
| `frontend` | Angular 21, TypeScript, Leaflet | UI, authentication, map, reservation, ride, summary, and reporting |
| `rental-service` | Java 21, Spring Boot 3.2.5 | Public APIs, domain state, Camunda workers, integrations, and SSE |
| `orchestration` | Camunda 8.9.1 all-in-one | BPMN process execution, Zeebe, and Camunda web applications |
| `bank-service` | Jolie, SOAP | Balance, deposit reservation, release, and charges |
| `bank-account-registration-service` | Jolie, REST | Bank-account creation |
| `stations-service` | Java 21, Spring Boot | Station catalog and unlock, lock, and charging simulation |
| `maintenance-service` | Java 21, Spring Boot | Maintenance queue and simulation |
| `fm-gateway` | Java 21, Spring Boot | Fleet Management REST API and route simulation |
| `tracking-service` | Java 21, Spring Boot, gRPC | Position state of tracked vehicles |
| `battery-service` | Java 21, Spring Boot, gRPC | Battery state of tracked vehicles |
| `rental-service-db` | PostgreSQL 18 | Users, BPMN sessions, fleet, reservations, and rentals |
| `bank-service-db` | PostgreSQL 18 | Accounts, balances, reserved amounts, and banking tokens |
| `redis` | Redis | Pub/Sub between workers, SSE notifications, and balance updates |
The Rental Service deploys three processes:
| File | Purpose |
|---|---|
| `rental_process.bpmn` | Instant rental, reservation, payment, telemetry, ride completion, reporting, and charging |
| `maintenance_process.bpmn` | Daily discovery and queuing of vehicles requiring maintenance |
| `update_status.bpmn` | Asynchronous vehicle status and battery updates |
The Camunda runtime uses a maximum of 2 GB of memory in Compose. Zeebe data and file-based H2 secondary storage are kept in Docker volumes.
Compose creates three networks:
- `acme-net`, for application services;
- `camunda`, for orchestration and the Rental Service;
- `fleet-management-net`, for the gateway, tracking, and battery services.
Persistent data is separated into volumes for Rental PostgreSQL, Bank PostgreSQL, Zeebe, and Camunda. Re-creatable volumes are also available for the Maven cache, `target` output, `node_modules`, and the Angular cache.
```text MSA-Project/ ├── README.md # main documentation ├── LICENSE ├── GUARD.md # Camunda-driven Angular navigation ├── MAINTENANCE.md # maintenance subsystem analysis ├── ISSUES.md # risks, limitations, and open issues ├── docs/ │ ├── asmP_Specifiche_progetto_2025-26.md │ ├── choreography.md # global choreography │ ├── general_diagram_doc.bpmn # documentary BPMN │ └── projections/ # participant projections ├── project-documentation/TinySOA/ # UML/Sirius models and TinySOA profile └── acme-mobility/ ├── docker-compose.yml ├── application.yaml # Camunda configuration ├── .env # Camunda version and local configuration ├── frontend/ ├── rental-service/ │ ├── db/init.sql │ └── src/main/resources/bpmn/ ├── bank-service/ ├── bank-account-registration-service/ ├── stations-service/ ├── maintenance-service/ └── fleet-management-service/ ├── fm-gateway/ ├── tracking-service/ └── battery-service/ ```
For frontend details, the `src/app` directory is organized into `core` (guards, services, and models), `features` (login, registration, map, reservation, and rental), and `shared` (header, map, and reusable components).
Java services follow the Spring separation between controllers, services, integrations, DTOs, configuration, and tests. The Rental Service adds JPA repositories, domain models, process-session management, and Camunda job workers.
- Docker Desktop or Docker Engine with Docker Compose v2.
- At least 8 GB assigned to Docker is recommended for the full stack.
- The ports listed in the next section must be available.
- An Internet connection is required on the first build to download images and dependencies.
On ARM64 hosts, the two Jolie containers run under `linux/amd64` emulation, so the first startup may take longer.
From the repository root:
```bash cd acme-mobility docker compose up --build ```
The command builds local images, starts all backend services, and then starts the frontend. Compose waits for dependency health checks before exposing the UI.
When the `frontend` container is ready, open:
- application: http://localhost:4200
- Camunda interface: http://localhost:8083
For the Camunda interface, local credentials are `demo` / `demo`.
To run in the background:
```bash docker compose up -d --build ```
If images, Dockerfiles, and dependencies have not changed:
```bash docker compose up ```
or:
```bash docker compose up -d ```
```bash docker compose down ```
This removes project containers and networks, but preserves databases, Camunda state, and caches in volumes.
```bash docker compose down -v ```
The `-v` option also removes all named project volumes. At the next startup, the following are recreated:
- users, accounts, reservations, and rentals;
- initial fleet data;
- Camunda/Zeebe state;
- Maven and Angular caches;
- container `node_modules`.
Use `down -v` when a clean environment is needed, not as a simple stop when data should be preserved.
```bash docker compose ps docker compose logs -f ```
To follow only the main components:
```bash docker compose logs -f orchestration rental-service frontend ```
The common policy is described in LOGGING.md. The project uses the standard logging-level mechanism and starts with `APPLICATION_LOG_LEVEL=INFO`, configured in `acme-mobility/.env`: only essential domain events, warnings, and errors are shown. To disable application logs:
```bash APPLICATION_LOG_LEVEL=OFF docker compose up ```
To temporarily enable jobs, service-to-service calls, and telemetry as well:
```bash APPLICATION_LOG_LEVEL=DEBUG docker compose up ```
| Host port | Service | Purpose |
|---|---|---|
| 4200 | Frontend | Angular application |
| 8080 | Rental Service | `/api`, SSE, and health APIs |
| 8082 | Account Registration | REST `POST /createAccount` |
| 8083 | Camunda | Camunda UI and APIs |
| 8084 | Stations Service | Station REST APIs |
| 8085 | Maintenance Service | Maintenance REST APIs |
| 8000 | Bank Service | SOAP endpoint |
| 8091 | Fleet Management Gateway | Monitoring REST API |
| 8092 | Tracking Service | HTTP/health port |
| 8093 | Battery Service | HTTP/health port |
| 26500 | Zeebe | gRPC gateway |
| 9600 | Camunda management | Health and management |
| 5432 | Rental PostgreSQL | Application database |
| 5433 | Bank PostgreSQL | Banking database |
| 6379 | Redis | Pub/Sub |
The APIs used directly by the frontend are:
```text /api/auth/* login, session, logout, and registration /api/rentals/map stations and vehicles /api/rentals/resume BPMN phase and canonical route /api/rentals/scan simulated QR scan /api/rentals/book reservation by station and type /api/rentals/bookings reservation details /api/rentals/undoBooking cancellation /api/rentals/end ride completion /api/rentals/report damage submission /api/rentals/report/no-report /api/notifications SSE stream /createAccount Jolie account creation ```
The containerized frontend proxies `/api` to `rental-service:8080` and `/createAccount` to `bank-account-registration-service:8080`.
With new volumes, SQL scripts create:
- 7 real stations in Bologna;
- 99 total vehicles, 33 per category;
- available vehicles and a few initial maintenance cases;
- demonstration accounts with sufficient, insufficient, or missing balance;
- demonstration users for the main scenarios.
An account that can be used immediately is:
```text email: [email protected] password: password123 balance: €100 ```
It is also possible to register a new user through the UI; the corresponding account starts with €100.
Compose already starts the frontend. To work with Angular directly on the host, start the stack, free port 4200, and use Node 22.12–24:
```bash cd acme-mobility docker compose up -d --build docker compose stop frontend
cd frontend npm ci npm start ```
`npm start` uses the host proxy:
- `/api` → `http://localhost:8080\`;
- `/createAccount` → `http://localhost:8082\`.
The container instead uses `npm run start:docker`, Compose hostnames, and file polling. To recreate the Angular cache:
```bash npm run clean:cache ```
```bash cd acme-mobility docker compose config --quiet ```
```bash cd acme-mobility/frontend npm ci npm run build npm test -- --watch=false --browsers=ChromeHeadless ```
JDK 21 is required:
```bash cd acme-mobility/rental-service mvn test
cd ../stations-service mvn test
cd ../maintenance-service mvn test
cd ../fleet-management-service/fm-gateway mvn test
cd ../tracking-service mvn test
cd ../battery-service mvn test ```
- Start with `docker compose up --build`.
- Register a new user or use the demo account.
- Verify the map.
- Complete an instant rental until arrival.
- End the ride and complete the no-damage branch.
- Verify the summary and return to the map.
- Make a reservation, then try cancellation and collection.
- Repeat a ride while submitting a damage report.
- Check that containers remain `healthy` and do not restart unexpectedly.
The projections cover the user, Rental Service, bank, stations, Fleet Management, Tracking Service, Battery Monitoring Service, and Maintenance Service.
The TinySOA directory contains UML models, Sirius diagrams, and the TinySOA profile for rental, rental startup, reservation cancellation, money reservation, maintenance, and periodic vehicle checks.
- GUARD.md: contract between BPMN state, backend, and Angular routing.
- LOGGING.md: log levels, format, and maintenance criteria.
- MAINTENANCE.md: semantics and limits of the maintenance flow.
- ISSUES.md: register of technical and functional issues.
The implementation covers the required participants and technologies:
- ACME Mobility capabilities orchestrated with Camunda;
- Jolie bank reached by the BPMS through SOAP;
- stations and Fleet Management exposed through REST;
- Fleet Management divided at least into tracking and battery monitoring;
- services distributed as containerized applications;
- choreography, projections, BPMN, and TinySOA SOA modeling.
Some aspects are deliberately simulated or do not yet perfectly match the domain description:
- the app is a responsive web application, not a native mobile app;
- QR scanning uses a vehicle ID rather than a camera;
- the reservation selects a type at a station and the backend assigns the vehicle with the highest battery level, rather than allowing selection of a specific vehicle;
- the current price uses a fixed fee and simulated time, rather than the distance required by the specification;
- completed rentals are persisted, but the UI has no history page;
- GraphHopper is used for route geometry, with a linear fallback;
- the maintenance queue and deduplication are in memory and are rebuilt from the Rental Service after a restart;
- local Camunda secondary storage uses H2 and the configuration is intended for development/demonstration, not production.
For concurrency, security, banking idempotency, callback, and resilience risks, consult ISSUES.md. Current code and tests remain the source of truth for the implementation.