Trading

Scope and Context

The trading service manages player credits inside a game. It opens and maintains bank accounts, issues vouchers that consume credits, and processes resource sales that add credits.

Trading integrates with external domains through events:

  • Game (external): drives bank-account creation when players join games.
  • Player (external): replicated for identity and traceability.
  • Robot (external): delivery events trigger resource sale handling and credit deposits.

Aggregates and Responsibilities

BankAccount

The BankAccount aggregate manages balance changes for a unique (gameId, playerId) pair.

  • Opens accounts for players in games.
  • Deposits credits (for example from resource sales).
  • Withdraws credits (for example for voucher purchases).
  • Enforces credit and balance invariants.

Credits are treated as an in-game value type with arithmetic and comparison rules. From a domain perspective, credits are created/destroyed by operations in this service, rather than transferred through an external monetary system.

Voucher

The Voucher aggregate is the entry point for purchase intents.

  • Supports purchase flows for:
    • ConstructRobotVoucher
    • UpgradeRobotVoucher
  • Validates request input.
  • Computes total costs.
  • Withdraws credits through BankAccount.
  • Persists and emits issued vouchers.

Voucher-related constraints include:

  • Construction vouchers require at least one robot.
  • Upgrade vouchers require an allowed upgrade level range (currently 1..5).
  • Robot existence/capability checks are not performed by trading.

Prices / Pricing

The Prices (Pricing) component provides price information for purchasable/sellable items and is used by resource sale flows. Pricing logic is separated from account mutation logic.

  • Maintains resource value definitions.
  • Resolves credit value for delivered resources.
  • Prices are initialized with defaults at game creation, then controllable via rest.
  • Publishes price-change related events

Core Use Cases

Opening a Bank Account (Automated)

When a player joins a game, trading opens a bank account with the configured starting balance.

Withdrawing Credits

Credits are withdrawn for paid actions (for example voucher purchases). Trading checks:

  • Account exists for the player in the game.
  • Balance is sufficient.

On success, trading emits:

  • CreditsWithdrawn
  • BankAccountECST

On failure (for example insufficient credits), the caller receives a failure outcome and can inform the player.

Depositing Credits

Credits are deposited when players earn value, for example through resource deliveries.

On success, trading emits:

  • CreditsDeposited
  • BankAccountECST

Buying Vouchers

Voucher purchase flow:

  1. Player intent is validated by Voucher.
  2. Total price is calculated.
  3. BankAccount withdrawal is attempted.
  4. If successful, the voucher is persisted and emitted.
  5. If withdrawal fails, voucher issuance is rejected.

Typical emitted events include:

  • ConstructRobotVoucherIssued (or upgrade equivalent)
  • CreditsWithdrawn (triggered through account mutation)
  • BankAccountECST (triggered through account mutation)

Selling Resources

  1. Robot delivers resource
  2. Price is calculated
  3. Credits are deposited

Typical emitted events include:

  • ResourcesSold
  • CreditsDeposited
  • BankAccountECST

Invariants and Business Rules

BankAccount Invariants

  • Balance must never become negative.
  • Credits are always positive values.
  • A withdrawal must be at least one credit.
  • There is at most one bank account per player and game.

Cross-Aggregate Rules

  • Trading reacts to game events to create/open bank accounts.
  • Trading assumes upstream events represent valid game/player existence.
  • Trading does not validate robot ownership/existence for voucher upgrade targets.

Eventing and Integration Patterns

  • Outgoing events generally extend a shared base event type to provide common headers.
  • Event payloads are defined within the event type and exposed via builder/factory methods.
  • Most events are published through a WriteOutbox interface backed by Postgres.
  • DLT publication is a notable exception where no transaction context may exist.

Functional Dependencies

Trading depends on inbound events from:

  • Game service to detect participation and initialize bank accounts.
  • Robot service to process resource deliveries and award credits.

It publishes outbound domain events so other services can react to financial state changes and vouchers.

Last modified December 3, 2025: Add Trading Docs (059821e)