Skip to content
Puneethkumar CK edited this page Mar 17, 2026 · 1 revision

FAQ

Frequently asked questions about the StableBridge platform.


General

What is the "sandwich" model?

StableBridge converts sender fiat (e.g., USD) to USDC stablecoin, transfers it on-chain via Base L2, then converts back to recipient fiat (e.g., EUR). The stablecoin layer is "sandwiched" between two fiat legs, providing fast, low-cost settlement.

What's the MVP corridor?

US → DE (USD → EUR) using:

  • On-ramp: Stripe ACH (fiat collection)
  • Chain: Base L2 (USDC transfer)
  • Off-ramp: Modulr SEPA (fiat payout)

Why stablecoins instead of SWIFT/correspondent banking?

Traditional StableBridge
Speed 2-5 business days Hours (with instant rails)
Cost $25-50 per transfer < $5
Transparency Opaque intermediary chain On-chain verifiable
Settlement T+2 to T+5 Near real-time on-chain

Architecture

Why hexagonal architecture?

Domain logic must be testable without Spring, JPA, or any infrastructure. The hexagonal pattern ensures domain purity, enables easy testing, and allows swapping infrastructure (e.g., changing from Stripe to another PSP) without touching business logic.

Why Temporal instead of Kafka Streams for orchestration?

Temporal provides durable execution — workflows survive pod restarts, infrastructure failures, and long-running async operations (e.g., waiting for ACH settlement). It has built-in saga compensation, retry policies, and visibility via the Temporal Web UI. Kafka Streams is better for stateless stream processing, not complex multi-step orchestrations.

Why a separate database per service?

Data isolation enables independent deployment, scaling, and technology evolution per service. It prevents tight coupling through shared schemas. Services share data via Kafka events, not direct database access.

Why Namastack for the outbox pattern?

Namastack provides a production-ready transactional outbox with retry, batching, and dead-letter handling. It reduces boilerplate compared to a custom implementation and has been validated in production systems.


Development

How do I add a new service?

See the Project Structure page for the tri-module layout. Key steps:

  1. Create 3 modules: -api, -client, main service
  2. Apply convention plugins in build.gradle.kts
  3. Write ArchitectureTest.java first
  4. Create Flyway migration for initial schema
  5. Implement domain → infrastructure → application layers

How do I add a new external provider?

Follow the External Provider Adapter Pattern in Design Patterns:

  1. Create package-private ACL DTOs in infrastructure/provider/<name>/
  2. Implement the domain port interface
  3. Add @CircuitBreaker annotation
  4. Configure via @ConfigurationProperties
  5. Write WireMock-based unit tests
  6. Add @ConditionalOnProperty for provider selection

How do I add a new Kafka topic?

  1. Add static final String TOPIC to your domain event record
  2. Create/update the outbox handler to route the event type
  3. Create consumer in the target service with @KafkaListener
  4. Ensure consumer is idempotent (check aggregate status before processing)
  5. Document the topic in Event Driven Architecture

Why can't I use any() in Mockito?

Generic matchers like any() give zero confidence about what was actually called. They mask bugs where the wrong arguments are passed. We require actual values in stubs and eqIgnoringTimestamps/eqIgnoring for verifications. See Testing Standards section 4.

Why no application service layer?

Controllers call domain CommandHandlers directly. An intermediate application service adds unnecessary indirection. The CommandHandler owns all business logic including transaction boundaries. Controllers are thin DTO-mapping shells. See Coding Standards section 3.


Testing

What test tiers are required?

Change Type Required Tests
Domain logic Unit tests (BDDMockito)
New REST endpoint Unit + Integration (MockMvc)
New repository method Integration (Testcontainers)
External provider integration Unit (WireMock)
New Kafka consumer Integration test
End-to-end flow Business test

How do I run only one service's tests?

make test-merchant-iam-all     # All tiers for one service

Or via Gradle:

./gradlew :merchant-iam:merchant-iam:test                # Unit
./gradlew :merchant-iam:merchant-iam:integrationTest     # Integration
./gradlew :merchant-iam:merchant-iam:businessTest        # Business

Operations

How do I debug a stuck payment?

  1. Open Temporal Web UI (localhost:8233)
  2. Search for workflow ID payment-{paymentId}
  3. Check which activity/signal the workflow is waiting for
  4. Check the relevant service logs for errors
  5. Check if external provider webhook was received

How do I check Kafka consumer lag?

Open Redpanda Console at localhost:9090 and check consumer groups, or use:

kafka-consumer-groups --bootstrap-server localhost:9092 --describe --group <service-group>

How do I reset my local database?

make infra-destroy    # Removes all volumes
make infra-up         # Recreates with fresh init.sql

Related Pages

Clone this wiki locally