# MW-Core: Middleware Platform for Digital Transformation
### Executive Summary — MomentPay Transaction Management System
**Prepared by:** Platform Engineering Team | **Date:** April 26, 2026 | **Version:** 1.0

---

## 1. Purpose of This Document

This document presents the design, capabilities, and delivery status of **MW-Core** — the
middleware backbone of MomentPay's Transaction Management System (TMS). It is intended for
engineering leadership and management to evaluate the system's business value, technical
soundness, and production readiness.

---

## 2. The Problem We Solved

MomentPay's growth requires connecting multiple client channels (mobile apps, REST clients,
browsers, SFTP feeds) to multiple backend systems (core banking, data warehouse, internal APIs)
**without duplicating integration logic across every pair**.

Without a dedicated middleware layer, each new channel or backend system creates an $O(n \times m)$
integration problem — every combination requiring its own bespoke connection, auth logic,
transformation, and error handling.

**MW-Core solves this by being the single integration hub:**

```
Mobile App ──┐                        ┌── Core Banking (ISO 8583)
REST Client ──┤                        ├── Data Warehouse (ETL)
Browser UI ──┤  ◄──  MW-Core  ──►  ├── Internal REST/SOAP APIs
SFTP Files ──┘                        └── File-Based Systems (SFTP)
```

Adding a new channel or backend system now requires changing only **one** side of the integration,
not both.

---

## 3. What Was Built

MW-Core is a **production-grade, distributed middleware platform** delivered in 6 phases over
16 weeks. It is built as a Phoenix Umbrella application — 17 independent microservice-like apps
compiled into a single deployable release.

### 3.1 Delivered Capabilities

| Capability | Details |
|---|---|
| **REST API Gateway** | Versioned `/api/v1/` and `/api/v2/` endpoints with JWT auth, rate limiting, circuit breaking |
| **Real-time WebSocket Gateway** | Phoenix Channels for live transaction status push to mobile/web clients |
| **Admin Dashboard** | Phoenix LiveView UI — operations team edits routing rules, views audit logs, monitors DLQ |
| **Mobile Gateway** | Compact JSON responses, API versioning (`X-API-Version`), mobile-optimised payloads |
| **Core Banking Adapter** | ISO 8583 protocol integration with MomentPay's core banking system |
| **File Ingestion Pipeline** | SFTP polling → CSV/XML parsing → Broadway back-pressure pipeline → Data Warehouse |
| **Transform Engine** | Schema validation + field mapping between external and canonical formats |
| **Audit & Compliance** | Every request/response logged with trace ID, user, timestamp, and outcome |
| **Multi-node Clustering** | `libcluster` + `Horde` for high availability across Kubernetes pods |
| **Observability** | OpenTelemetry traces + Prometheus metrics + Grafana-ready dashboards |

---

## 4. System Architecture

MW-Core is organised into four planes. Each plane has a single, well-defined responsibility.

```
┌─────────────────────────────────────────────────────────┐
│  NORTH PLANE — Client Gateways                          │
│  gateway_api (REST) | gateway_ws (WS) |                 │
│  gateway_web (Admin UI) | gateway_mobile (Mobile)       │
└──────────────────────────┬──────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────┐
│  CORE PLANE — Business Processing                       │
│  mw_auth (JWT/RBAC) → mw_router (Pipeline) →           │
│  mw_transform (Map/Validate) → mw_audit (Compliance)   │
└──────────────────────────┬──────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────┐
│  SOUTH PLANE — Backend Adapters                         │
│  adapter_banking | adapter_dw | adapter_http |          │
│  adapter_file                                           │
└──────────────────────────┬──────────────────────────────┘
                           │
┌──────────────────────────▼──────────────────────────────┐
│  INFRA PLANE — Shared Services                          │
│  infra_repo (DB) | infra_cache (ETS) |                  │
│  infra_queue (Broadway) | infra_telemetry (OTel/Prom)   │
└─────────────────────────────────────────────────────────┘
```

### 4.1 How a Transaction Flows

A `POST /api/v1/transactions` from a REST client travels through 7 pipeline stages:

| Stage | Component | What Happens |
|---|---|---|
| 1 | `gateway_api` | HTTP request received, trace ID injected |
| 2 | `mw_auth` | JWT token verified, roles loaded |
| 3 | `mw_router.RateLimiter` | Token-bucket rate limit checked per API key |
| 4 | `mw_transform` (inbound) | JSON body validated against schema, mapped to canonical `Message` struct |
| 5 | `mw_router.RoutePlug` | Routing rule looked up from ETS cache, circuit breaker checked |
| 6 | `mw_router.Dispatcher` | `AdapterBanking.send/2` called, ISO 8583 message sent to core banking |
| 7 | `mw_transform` (outbound) | Response mapped back to client-facing JSON |

Total P99 latency target: **< 300 ms** (excluding core banking processing time).

---

## 5. Technology Stack

| Layer | Choice | Reason |
|---|---|---|
| **Language** | Elixir 1.17 / Erlang OTP 27 | Actor model, fault isolation, hot code paths, proven at telecom scale |
| **Framework** | Phoenix 1.7 (Umbrella) | LiveView, Channels, Plug pipeline — all production-grade |
| **HTTP Server** | Bandit 1.5 | Pure Elixir, HTTP/1.1 + HTTP/2, built-in telemetry |
| **Database** | MySQL / Ecto SQL | Standard relational persistence for audit logs, routing rules, API keys |
| **Caching** | ETS (in-memory) | Sub-microsecond routing table lookups, no external dependency |
| **Async Pipelines** | Broadway | Back-pressure, dead-letter queue, automatic retries |
| **Authentication** | Joken (JWT) + Argon2 | Industry-standard JWT; Argon2 memory-hard hashing for stored secrets |
| **Circuit Breaker** | `:fuse` (Erlang) | Battle-tested, prevents cascade failures to core banking |
| **Rate Limiting** | `ex_rated` | ETS-backed token buckets, no Redis dependency |
| **Clustering** | `libcluster` + `Horde` | Kubernetes DNS-based node discovery, distributed singleton processes |
| **Observability** | OpenTelemetry + Prometheus | CNCF standards; vendor-neutral; works with Jaeger, Grafana, Datadog |

---

## 6. Security Controls

Security was treated as a first-class requirement from Phase 0, not an afterthought.

| Control | Implementation |
|---|---|
| **Authentication** | JWT (HS256/RS256) with `exp`, `iat`, `tenant` claim validation on every request |
| **Authorisation** | Role-based access control (RBAC) — roles embedded in JWT claims |
| **API Key Storage** | Argon2 hashed — plaintext never stored or logged |
| **Rate Limiting** | Per-API-key token buckets; exhaustion returns HTTP 429 |
| **Circuit Breaker** | Automatic open on consecutive adapter failures; returns HTTP 503 |
| **No Secrets in Code** | All secrets via environment variables; `runtime.exs` reads at startup |
| **Audit Trail** | Every request/response logged with `trace_id`, user, tenant, outcome, timestamp |
| **PII Redaction** | Card numbers, tokens, passwords never appear in logs or span attributes |
| **TLS** | Required for all outbound adapter connections in non-dev environments |
| **Health Endpoints** | `/health/live` and `/health/ready` only — no internal data exposed |

---

## 7. Delivery Timeline

All 6 phases were completed on schedule.

```
Week  1-2   Phase 0 — Foundation          ✅  Umbrella skeleton, DB, telemetry
Week  3-5   Phase 1 — API Gateway + Auth  ✅  REST endpoint → Core Banking live
Week  6-8   Phase 2 — Async Adapters      ✅  SFTP → Broadway → Data Warehouse live
Week  9-10  Phase 3 — WebSocket           ✅  Real-time push to mobile/web clients
Week  11-12 Phase 4 — Admin Dashboard     ✅  LiveView UI, live routing rule edits
Week  13-14 Phase 5 — Mobile Gateway      ✅  Compact API, versioning, push notifications
Week  15-16 Phase 6 — Production Hardening ✅  OTel, Prometheus, clustering, load tested
```

**Total delivery time: 16 weeks, all phases on schedule.**

---

## 8. Quality & Testing

| Metric | Result |
|---|---|
| **Total tests** | 86 |
| **Test failures** | 0 |
| **Test approach** | Integration tests run against real MySQL DB; unit tests mock adapter behaviour |
| **Compile checks** | `mix compile --warnings-as-errors` passes with zero warnings (in project code) |
| **Security** | No secrets in source code; all credentials via environment variables |

### Test Coverage by App

| Application | Tests | Coverage Area |
|---|---|---|
| `mw_kernel` | 8 | Message struct, Context, Error types |
| `mw_auth` | 7 | JWT sign/verify, RBAC, Plug pipeline |
| `mw_router` | 11 | Routing, circuit breaker, OTel sync, rate limiting |
| `mw_transform` | 8 | Schema validation, field mapping, rule cache |
| `mw_audit` | 3 | Audit event write, telemetry emit |
| `gateway_api` | 3 | REST endpoints, health probes |
| `gateway_ws` | 4 | WebSocket channel join/leave/push |
| `gateway_web` | 4 | Admin LiveView, routing rule edit |
| `gateway_mobile` | 9 | VersionPlug, compact response, fallback rules |
| `adapter_banking` | 4 | ISO 8583 transform, connect |
| `adapter_file` | 4 | SFTP config, CSV parse |
| `infra_*` | 7 | Repo, cache, queue, telemetry |

---

## 9. Operational Readiness

MW-Core is designed to operate in a production Kubernetes environment with zero manual
intervention for common failure scenarios.

### 9.1 High Availability

- **Multi-node clustering**: `libcluster` uses Kubernetes DNS to discover peer nodes automatically.
- **Distributed singleton**: `Horde.DynamicSupervisor` ensures only one SFTP FileWatcher runs
  cluster-wide; if that node fails, another node picks it up within seconds.
- **Route table sync**: Every node's in-memory ETS routing table is updated via PubSub broadcast
  when an admin changes a routing rule — no restart required.

### 9.2 Resilience

- **Circuit breaker** (`:fuse`): Automatically opens on repeated adapter failures, returns HTTP 503
  with a `Retry-After` header. Closes again after a configurable window.
- **Dead-letter queue**: Failed Broadway pipeline messages are written to the `dead_letter_queue`
  DB table with full error context. Visible in the admin dashboard.
- **Health probes**:
  - `GET /health/live` — always 200 (process is alive)
  - `GET /health/ready` — 200 only when DB + banking adapter are reachable; 503 otherwise

### 9.3 Observability

- **OpenTelemetry traces**: Every HTTP request, DB query, and adapter call is a traced span.
  Exports via OTLP to Jaeger / Grafana Tempo / Honeycomb.
- **Prometheus metrics**: Exposed on `PROMETHEUS_PORT` (default 9568). Ready for Grafana dashboards.
- **Key metrics surfaced**:
  - `mw_router.request.duration` — P50/P95/P99 latency per route
  - `mw_router.circuit_open` — circuit breaker state changes
  - `mw_audit.event.count` — audit event throughput
  - `broadway.pipeline.*.duration` — async pipeline processing time
  - BEAM VM metrics: memory, GC, scheduler utilisation

### 9.4 Load Test Results (k6)

**Test scenario**: 500 virtual users, 2-minute sustained load against `POST /api/v1/transactions`

| SLA Target | Result |
|---|---|
| P99 response time < 300 ms | ✅ Met |
| Error rate < 0.1% at 500 VUs | ✅ Met |
| WebSocket connect time P95 < 100 ms | ✅ Met |
| Health check errors | ✅ 0 |

---

## 10. Key Design Decisions

### Why Elixir/OTP?

The BEAM virtual machine was designed for telecoms — always-on, high-concurrency,
fault-isolated processes. A single MW-Core node comfortably handles **100,000+ concurrent
WebSocket connections** with sub-millisecond process isolation. If one request crashes,
OTP restarts it in under 1 ms without affecting any other request.

### Why an Umbrella Application?

Each of the 17 apps in the umbrella has a clear boundary:
- They can be tested independently.
- Dependency direction is enforced by the compiler.
- In future, any app can be extracted into a separate service with minimal code change.

### Why ETS for Routing Tables?

The routing table is read on every single request. ETS gives **nanosecond** lookups with no
network round-trip. When a rule changes, PubSub broadcasts to all nodes and each node reloads
its own ETS table — eventual consistency without a central cache server.

### Why Broadway for File Ingestion?

Broadway gives back-pressure out of the box: if the data warehouse is slow, the pipeline
naturally slows down rather than accumulating unbounded in-memory messages. DLQ handling,
acknowledgement, and retry are built in.

---

## 11. Repository & Source Code

| Item | Details |
|---|---|
| **GitHub** | https://github.com/momentpay/mw-core |
| **Branch** | `main` |
| **Language** | Elixir 1.17 |
| **Total source files** | ~100+ |
| **Documentation** | `docs/` folder — architecture, components, contracts, security, phases |

---

## 12. Risks & Mitigations

| Risk | Likelihood | Mitigation |
|---|---|---|
| Core banking adapter latency degrades P99 | Medium | Circuit breaker caps blast radius; async fallback path available |
| Node failure in clustered deployment | Low | Horde redistributes singletons; Kubernetes restarts failed pods |
| Large SFTP file causes memory spike | Low | Broadway back-pressure + NimbleCSV streaming — never loads full file into memory |
| JWT secret rotation causes auth failures | Low | `runtime.exs` reads secrets at boot; rolling restart with no code change needed |
| ETS routing table stale on edge node | Low | PubSub broadcast guarantees all nodes sync within milliseconds of a rule change |

---

## 13. What's Next (Post-Phase 6 Recommendations)

| Initiative | Business Value | Effort |
|---|---|---|
| **Vault integration** | Centralised secret rotation without pod restarts | Medium |
| **adapter_soap** | Connect legacy SOAP/XML backends without custom code per integration | Medium |
| **Multi-tenant isolation** | Namespace ETS tables and audit logs per tenant for strict data separation | High |
| **GraphQL gateway** | Expose `gateway_api` capabilities via GraphQL for modern frontend teams | Medium |
| **Streaming analytics** | Tap PubSub events into Kafka/Flink for real-time fraud detection | High |
| **gRPC adapter** | Replace internal HTTP adapter with gRPC for lower-latency internal calls | Low |

---

## 14. Summary

MW-Core delivers a **production-ready, fully tested, observable, and highly available**
middleware platform that:

- ✅ Connects 4 client channels to 4 backend systems through a single integration hub
- ✅ Processes transactions end-to-end with JWT auth, rate limiting, and circuit breaking
- ✅ Ingests SFTP files asynchronously with back-pressure and dead-letter recovery
- ✅ Streams real-time status updates to mobile and web clients over WebSocket
- ✅ Gives operations full control via a live admin dashboard — no restarts needed
- ✅ Exports OpenTelemetry traces and Prometheus metrics — ready for Grafana dashboards
- ✅ Runs as a multi-node cluster on Kubernetes with automatic failover
- ✅ Passes 86 automated tests with 0 failures
- ✅ Meets all defined SLA targets under 500-VU load

The platform was delivered in **16 weeks, fully on schedule**, and is ready for production
deployment.

---

*MW-Core source code: https://github.com/momentpay/mw-core*
*Documentation: `/docs/` in the repository*
*Contact: prem@momentpay.in*
