Ciphernance

Core banking engine built to learn distributed systems from the inside.

in active development

Six microservices coordinated by choreography-based saga, with eventually consistent ABAC authorization synchronized via Kafka. Event sourcing scoped to the Transaction Service. Audit Service immutable by design.

The Why

Ciphernance exists because reading about distributed systems is not the same as building one. Most learning material teaches sagas, event sourcing, and policy-based authorization in isolation, with toy examples that miss the friction of making them coexist in a real system.

Banking is a domain where these patterns have to coexist — transactions must be consistent under failure, authorization has to scale across services, audit trails are non-negotiable. So Ciphernance is built deliberately as a banking engine, with no shortcuts. Every architectural decision is documented in an ADR; every service has a defined role in the topology before code is written. The point is not to ship a product. The point is to understand the trade-offs of distributed architecture by living inside them.

Architecture at a Glance

API Gateway

Entry point — routes requests and delegates auth verification to Identity Service

Identity Service

ABAC policy authority — user identity, account lifecycle, and policy sync via Kafka

Wallet Service

Financial state — balance derived from event history, one Asset per Account

Transaction Service

Event-sourced saga origin — transaction history is the source of truth

Fraud Service

Real-time risk evaluation — subscribes to transaction events, produces risk signals

Audit Service

Immutable append-only event log — compliance, traceability, full system history

Click any service to see its role, responsibilities, key decisions, and connections.


Current Focus

The Identity Service is in active development. Domain modeling has moved past the User and Account aggregates; current work is on the ABAC policy structure — designing the YAML DSL that will be the single source of truth for authorization, with policy agents in each service receiving updates via Kafka.

What's Next

Once Identity Service stabilizes, work moves to the Wallet Service: financial state with balance derived from event history, one Asset per Account. Then Transaction Service, which is where Event Sourcing genuinely belongs and where the saga choreography becomes visible end-to-end. Fraud, Audit, and the API Gateway follow.

Key Architectural Decisions

Ciphernance has 15 ADRs documented in the repository. The five below carry the architectural thesis of the system.

ADR 01 — Choreography over Orchestration

Context:Saga coordination across six services needs a control mechanism. The two options are a central orchestrator that issues commands, or choreography where each service reacts to events from others.

Decision:Choreography. A central orchestrator becomes a coupling point and a single point of failure. Choreography accepts harder traceability as the cost — mitigated by the immutable Audit Service that captures the full event chain.

ADR 02 — Eventually Consistent ABAC with Distributed Policy Agents

Context:Authorization in a distributed system either lives in a central service every request hits, or is distributed with each service evaluating policies locally.

Decision:Distributed. Each service hosts a Policy Agent with a two-tier cache (Caffeine L1 local, Redis L2 shared). Policy updates originate in the Identity Service and propagate via Kafka. Authorization is eventually consistent — a deliberate trade-off for not coupling every service to a central authorization service.

ADR 03 — Event Sourcing Scoped to Transaction Service Only

Context:Event sourcing is often applied as a project-wide pattern, but it carries operational complexity that not every domain needs.

Decision:Event sourcing only in the Transaction Service. Other services use traditional CRUD with Kafka projections where needed. Transaction history is the one place where immutable audit trail is a domain requirement, not a pattern preference.

ADR 04 — YAML DSL for ABAC Policies (XACML-Inspired)

Context:Policy expression needs to be readable by humans, versionable in Git, and evaluated mechanically. XACML has the conceptual structure but XML is heavy.

Decision:YAML-based DSL inspired by XACML's PAP/PIP/PDP/PEP separation, but expressed in syntax that fits the team's tooling. Policies live in the repo, versioned and reviewed like code.

ADR 05 — Audit Service as Immutable Append-Only Log

Context:Compliance and traceability in a banking system require a log that cannot be tampered with, even by services that produce events.

Decision:Audit Service has no update or delete operations. It receives events from all services via Kafka and persists them in append-only storage. Reads are allowed; writes go through a single ingestion path.

Full ADR collection available in the GitHub repository.

Trade-offs Accepted

TRADE-OFF 1

Operational complexity for architectural clarity

Choreography saga, distributed policy caches, and event sourcing in one service mean more moving parts than a single monolith with a relational database. The cost is real — more failure modes, more observability surface area, more deployment coordination. The trade was deliberate: this project exists to understand those costs by living with them, not to avoid them.

TRADE-OFF 2

Eventually consistent authorization over strong consistency

Policy updates propagating via Kafka means there is a window where a service may evaluate a request against a stale policy. For a learning project, this is acceptable. For a real banking system, it would require additional safeguards (policy version checks, fallback to central service on cache miss). The architecture would extend; the core model would not need to change.

TRADE-OFF 3

Documentation discipline as up-front cost

Every architectural decision in Ciphernance has an ADR before code. This slows down early development. The bet is that the slowdown pays back when implementation begins — there is no architectural debate during sprints because the decisions are already made and recorded. So far, this has held.

Stack

RUNTIME
Java 21· Spring Boot 4· Spring Cloud Gateway· Spring Authorization Server
DATA
PostgreSQL· Redis· Neo4j· Kafka
OBSERVABILITY
Micrometer· Prometheus· Grafana
TESTING
JUnit 5· Testcontainers
BUILD
Maven (monorepo)

Resources