// Case study · Platform / architecture

Platform component framework

The shared libraries and contracts a payment platform's dozen services are built on — a typed object-mapper, domain primitives, query/criteria and versioned service contracts — so everything speaks the same language.

Software Developer → Team Lead · Across a payment-orchestration platform's services and libraries.

The problem

A payment platform isn't one service — it's many (gateway, processing, directory, facade, webhooks…). Without shared foundations each one reinvents DTOs, mapping, money handling and provider contracts, and they drift apart — every integration point becomes a translation bug waiting, with real money behind it.

The approach

I built and owned a set of shared components: a typed AutoMapper for DTO ↔ domain mapping, domain primitives (money, ids, value objects), a criteria / query abstraction, and versioned contracts between services (gate, processing, connector, directory). Services depend on the contracts, not on each other's internals.

The AutoMapper

The mapper is the interesting one: it handles nested DTO collections and maps, lazy copying, and custom operations (map from a source property, from a container / collection), with bounded caches and cycle detection so mapping deep, self-referential payment objects stays fast and safe.

The decision & trade-off

Investing in shared libraries and hard contracts slows the very first feature — you build the foundation before the house. But it made every later service cheaper, consistent and safe to change: a contract bump is a visible, versioned event, not a silent break in production.

Architecture

A layered set of packages — primitives → mapping / criteria → service contracts (gate / processing / connector / directory) → SDK bundles — that every service composes. Change flows through versioned contracts and generated mappings, not copy-paste.

Outcome

A dozen services on one shared vocabulary — DTOs, money, mapping and contracts that behave the same everywhere, so cross-service changes are boring instead of dangerous.

What I'd do differently

Codegen more of the contract and mapping boilerplate from the start — the AutoMapper's generated-mapping caching arrived later than it should have.

Architecture diagram

Platform Component Framework — Shared primitives, mapping and versioned contracts every service is built on. Component Framework [Software System] Domain Primitives[Container: Money, ids, value objects] Mapping / Criteria[Container: AutoMapper] Service Contracts[Container: gate, processing, connector, directory] SDK Bundles[Container] Platform Services[External System] Typed foundations Map DTO ↔ domain Versioned bundles Compose Platform Component Framework Shared primitives, mapping and versioned contracts every service is built on.

Stack

  • PHP
  • Symfony
  • DDD
  • CQRS

Have a similar problem?

Tell me about it — I reply within a day.