rl-docs-hub

Home · Apps · rl-bank-mvp · rl-main-infra · todo_api · todo_mobile


Architecture

flowchart TB
  Users[Customers + Merchant Portal Users]
  Auth0[Auth0 Tenant]

  subgraph Edge[Edge / Public]
    CFWeb[CloudFront\ncustomer web]
    CFMp[CloudFront\nmerchant portal]
    WAF[WAF optional phase 2]
  end

  subgraph AWS[AWS ap-southeast-1 / account 982233224911]
    subgraph VPC[VPC]
      ALB[Public ALB\napi.bank.example]
      ECS[ECS Fargate Service\nrl-bank-api]
      Cache[ElastiCache Serverless\nValkey]
      DB[(External DB via DB_URI\nor managed DB later)]
      SM[Secrets Manager]
      SSM[SSM Parameter Store]
      CW[CloudWatch Logs + Alarms]
    end

    S3Web[S3 bucket\ncustomer app build]
    S3Mp[S3 bucket\nmerchant portal build]
    ECR[ECR repo\nrl-bank-api]
    ACMEdge[ACM us-east-1\nCloudFront certs]
    ACMAlb[ACM ap-southeast-1\nALB cert]
    R53[Route53 Hosted Zone]
  end

  Users --> CFWeb
  Users --> CFMp
  Users --> ALB
  CFWeb --> S3Web
  CFMp --> S3Mp
  Users -. login .-> Auth0
  CFWeb -. tokens .-> Auth0
  CFMp -. tokens .-> Auth0
  ALB --> ECS
  ECS --> Cache
  ECS --> DB
  ECS --> Auth0
  ECS --> SM
  ECS --> SSM
  ECS --> CW
  ECS --> ECR
  R53 --> CFWeb
  R53 --> CFMp
  R53 --> ALB
  ACMEdge --> CFWeb
  ACMEdge --> CFMp
  ACMAlb --> ALB
  WAF -. attach later .-> CFWeb
  WAF -. attach later .-> CFMp

PBX / telephony note

If the fake-bank MVP exposes PBX telephony, voice-bot, or IVR entry points, treat those as part of the product safety surface rather than just an ops detail.

The standing documentation rule is:

See PBX IVR / Call Script for the canonical wording and routing guidance.

Domain and TLS assumptions

Assume one hosted zone already exists or will be created in Route53. Suggested public names:

TLS assumptions:

Auth0 callback/logout/web origin assumptions:

AWS services required for MVP

For rl-bank-api

For rl-bank-webapp

For rl-bank-mp-webapp

Network model

Reuse the rl-main-infra production VPC patterns where possible.

Public tier

Private app tier

Data/cache tier

API deployment recommendation

For MVP, deploy rl-bank-api as a single ECS Fargate service.

Why this is the right call:

Suggested runtime shape:

Important repo observation:

Web hosting recommendation

rl-bank-mp-webapp (React/Vite)

Host as a static SPA on S3 + CloudFront.

Needs:

rl-bank-webapp (Flutter web)

Also host on S3 + CloudFront.

Needs:

Opinionated note: for this MVP I would keep both web apps on the same hosting pattern rather than mixing Amplify Hosting for one and S3/CloudFront for the other. One pattern means less drift, easier Pulumi reuse, and clearer IAM.

Application-layer support workspace architecture

The next realism slice stays entirely inside the existing app/API architecture:

Support workspace data flow

sequenceDiagram
  participant Staff as Staff user
  participant Portal as rl-bank-mp-webapp
  participant API as rl-bank-api GraphQL
  participant Guard as Staff authz guard
  participant Account as Account service
  participant Audit as Auditlog service
  participant DB as Mongo collections

  Staff->>Portal: Open support workspace
  Portal->>API: adminListAccountsForSupport
  API->>Guard: validate account.read
  Guard-->>API: allowed
  API->>Account: list support accounts
  Account->>DB: read accounts + latest transactions
  DB-->>Account: account list
  Account-->>API: support list payload
  API-->>Portal: account queue

  Staff->>Portal: Submit freeze/unfreeze/close + reason
  Portal->>API: adminUpdateAccountStatus
  API->>Guard: validate account.manage
  Guard-->>API: allowed
  API->>Account: validate transition + update status
  Account->>DB: update accounts
  Account->>Audit: record servicing action
  Audit->>DB: insert audit_logs entry
  Account->>DB: read refreshed support detail
  DB-->>Account: refreshed context
  Account-->>API: support detail payload
  API-->>Portal: updated detail + audit-backed history

GraphQL and Auth0 considerations

Browser auth flow

API auth flow

rl-bank-api already has the right broad shape:

MVP requirements to formalize:

Auth0 app layout

Suggested Auth0 objects:

Environment, config, and secrets strategy

Follow the rl-main-infra config style: explicit Pulumi config for infrastructure decisions, app runtime config delivered through AWS-native services.

Split of responsibility

Pulumi config (infra:*)

Use for infrastructure decisions only, for example:

SSM Parameter Store (non-secret)

Use for runtime values that are not sensitive:

Secrets Manager (secret)

Use for anything credential-like:

ECS injection pattern

Naming convention suggestion

Database posture for MVP

The code currently only tells us this for sure:

That is a giant hint not to hard-code a database choice into the first infra pass.

My recommendation:

  1. Treat the database as an external dependency in MVP phase 1.
  2. Deliver DB_URI via Secrets Manager.
  3. Only codify AWS-managed DB resources after the app team confirms the authoritative engine.

If the engine is confirmed later:

For MVP planning today, forcing a DB resource before the app team confirms the engine is how infra gets stupid. Better to leave a clean interface now.

Least-privilege IAM plan

CI/CD deploy role for API image push

Needs only:

Scope recommendation:

CI/CD role for static site publish

Needs only:

Scope recommendation:

ECS task execution role

Needs only:

API task role

Start narrow. Only grant app runtime calls it actually makes. Likely set:

Route53/ACM management role

For infra Pulumi only:

ElastiCache scope

If creating serverless cache via Pulumi, infra role needs ElastiCache create/read/update for only the relevant cache resources. App runtime itself normally does not need AWS IAM for cache access; it connects over the VPC network using the endpoint/secret.

Pulumi structure proposal

To stay consistent with rl-main-infra, I would add a gated module rather than a one-off script pile.

Suggested module layout:

Suggested config shape:

infra:bankMvp:
  enabled: true
  createResources: false
  namePrefix: prd-rl-bank
  domain:
    rootDomain: example.com
    apiSubdomain: api
    appSubdomain: app
    merchantSubdomain: merchant
    hostedZoneId: Z123456789
  network:
    vpcId: vpc-...
    publicSubnetIds:
      - subnet-a
      - subnet-b
    privateSubnetIds:
      - subnet-c
      - subnet-d
  api:
    image: 982233224911.dkr.ecr.ap-southeast-1.amazonaws.com/rl-bank-api:latest
    containerPort: 9000
    desiredCount: 2
    healthCheckPath: /health
  webapp:
    bucketName: prd-rl-bank-webapp
  merchantPortal:
    bucketName: prd-rl-bank-mp-webapp
  auth0:
    domain: tenant.region.auth0.com
    audience: https://api.example.com

Pattern note:

Application slice extension: account detail + history realism

This application-layer slice sits on top of the same architecture above and does not require new infrastructure.

flowchart LR
  Customer --> CustomerApp[Customer web/mobile app]
  CustomerApp --> GraphQL[rl-bank-api /api/graphql]
  GraphQL --> AccountService[Account service]
  GraphQL --> TransactionService[Transaction service]
  AccountService --> Accounts[(accounts)]
  AccountService --> Cards[(cards)]
  AccountService --> Transactions[(transactions)]
  TransactionService --> Transactions

The main design choice is to compute account realism from existing persisted data rather than invent a separate account-history subsystem too early.

Rollout plan

Phase 0 — confirm unknowns

Phase 1 — Pulumi scaffold only

Phase 2 — static web foundations

Phase 3 — API runtime

Phase 4 — auth and app cutover

Phase 5 — hardening

Gaps / blockers to resolve before implementation

  1. Database engine is not confirmed.
  2. No explicit Dockerfile/runtime artifact reviewed yet for rl-bank-api.
  3. No existing bank domain/hosted zone confirmed yet.
  4. Need to decide whether bank resources live inside rl-main-infra or a dedicated rl-bank-infra repo that follows the same patterns.
  5. JWT verification should explicitly validate issuer/audience if not already done elsewhere in code.
  6. Need to verify whether customer Flutter web app should use real Auth0 browser login or remain demo-auth style during MVP.

My recommendation on repo shape

Short version:

My bias for this MVP: start inside rl-main-infra as a gated module set, because the patterns already exist and there is only one production AWS account.