Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/microservices-patterns/ftgo-application/llms.txt

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

The Accounting Service in the FTGO application manages customer payment accounts and handles payment authorization as part of distributed order transactions. It is the only service in the FTGO application that uses event sourcing via the Eventuate Client framework: the Account aggregate’s state is persisted entirely as a sequence of events rather than as a traditional relational row. This page covers the service’s responsibilities, domain model, REST API, messaging, and storage schema.

Responsibilities

  • Maintain event-sourced Account aggregates representing the payment account for each consumer
  • Handle AuthorizeCommand saga commands to authorize payment when an order is created
  • Handle ReverseAuthorizationCommand saga commands to reverse a payment when an order is cancelled
  • Handle ReviseAuthorization saga commands to adjust the authorized amount when an order is revised
  • Expose a REST endpoint to retrieve account information by ID

Domain Model

The Account aggregate extends ReflectiveMutableCommandProcessingAggregate<Account, AccountCommand> from the Eventuate Client library. State is derived entirely from the event stream — there is no accounts table in a traditional sense.

Commands processed

CommandEvent emitted
CreateAccountCommandAccountCreatedEvent
AuthorizeCommandInternalAccountAuthorizedEvent
ReverseAuthorizationCommandInternal(empty list — no event)
ReviseAuthorizationCommandInternal(empty list — no event)
The apply() methods rebuild aggregate state from events. AccountAuthorizedEvent triggers the apply(AccountAuthorizedEvent) method, which can update internal balance or authorization state. SagaReplyRequestedEvent is also applied (required by the Eventuate Tram sagas-eventsourcing integration).
Because Account uses event sourcing, queries against account state load the full event history from the Eventuate event store and replay it. The AggregateRepository<Account, AccountCommand> handles this transparently.

Account lifecycle

(AccountCreatedEvent applied)

   Active account

(AccountAuthorizedEvent applied on each authorization)
Authorization is idempotent with respect to saga retry — the replyingTo option in AccountingServiceCommandHandler ensures that replies are sent back and duplicate commands are handled by the saga framework.
The AccountDisabledException is a domain exception that causes the command handler to reply with AccountDisabledReply, which causes the CreateOrderSaga to fail and the order to be rejected.

REST API

The service listens on port 8085. All endpoints are under the /accounts path.
MethodPathDescription
GET/accounts/{accountId}Retrieve account information by ID. Returns 404 if the account entity does not exist in the event store.

Get account response body

{
  "accountId": "1"
}
The account ID is a String because Eventuate Client uses string-based aggregate IDs. The AccountsController maps the consumer’s numeric ID (as a string) to the Eventuate aggregate ID.

Messaging

The Accounting Service listens on the accountingService command channel and handles saga commands sent by the Order Service:
CommandHandler methodReply on successReply on failure
AuthorizeCommandauthorize()withSuccess()withFailure(AccountDisabledReply)
ReverseAuthorizationCommandreverseAuthorization()withSuccess()withFailure(AccountDisabledReply)
ReviseAuthorizationreviseAuthorization()withSuccess()withFailure(AccountDisabledReply)
All three handlers use the replyingTo(cm) option with .catching(AccountDisabledException.class, ...) so that a disabled account automatically generates the appropriate failure reply. The consumer ID from the incoming command is converted to a string to address the Eventuate AggregateRepository:
accountRepository.update(Long.toString(command.getConsumerId()), makeAuthorizeCommandInternal(command), ...);

Events published

The Accounting Service emits events into the Eventuate event store (not the Eventuate Tram message bus):
EventTrigger
AccountCreatedEventCreateAccountCommand is processed
AccountAuthorizedEventAuthorizeCommandInternal is processed
These events are stored in the Eventuate event store (events table) rather than the Tram outbox.

Database

The Accounting Service uses the Eventuate event store (MySQL-backed by default in the FTGO docker-compose setup) rather than a traditional domain schema.
TableDescription
eventsEventuate event store — all Account aggregate events
entitiesEventuate entity registry — tracks the latest event position per aggregate ID
snapshotsOptional snapshots for aggregate replay optimization
Because the Accounting Service uses event sourcing, you can inspect the full authorization history for any consumer account by querying the events table where entity_type = 'net.chrisrichardson.ftgo.accountingservice.domain.Account' and entity_id = '<consumerId>'.

Build docs developers (and LLMs) love