Studies
Decision (ADR)Fintech (cenário)ArquiteturaAccepted

ADR: Modular Monolith vs Microservices in a Greenfield Fintech

Jan 15, 2026 9 min AI-assisted
Share:

Listen to study

generated on play

Generated only on first play

On demand
0:000:00
Speed
The MP3 is saved to S3 after the first play.

An early-stage fintech faces the classic architecture decision: go straight to microservices or build a modular monolith first. This ADR examines the real forces at play — team size, validation speed, blast radius, and operational cost — and records the decision with its concrete consequences.

Microservices sell well at conferences. Modular monoliths ship products. For a greenfield fintech with a small team and an unconsolidated domain, the choice of service topology is one of the most consequential decisions of the first months — and one of the most frequently gotten wrong for the wrong reasons.

Scenario Fact Sheet

Company / System
Greenfield fintech (composite scenario based on recurring market patterns)
Stage
Pre-product / MVP under construction
Engineering team size
4–7 full-stack/backend engineers, 1 tech lead
Domain
Payments, KYC onboarding, account management — boundaries still being discovered
Candidate stack
Node.js / TypeScript or Java 21, PostgreSQL, AWS (ECS or EKS), eventual event bus
Regulatory constraints
Bacen / PCI-DSS Level 1 on the horizon; auditability and sensitive data segregation mandatory
Time to first paying customer (estimate)
6–9 months
Decision recorded at
Inception phase / initial design sprint

Context and the Forces at Play

Every architecture decision is a function of the forces acting on the system at a given moment. Ignoring those forces and applying a pattern because it is "modern" or because the industry reference company uses it is one of the most common ways to accumulate premature technical debt.

In this scenario, the forces are clear and, in part, contradictory:

Business validation speed. The fintech does not yet know exactly what its real bounded contexts are. The payments domain seems obvious, but the boundaries between "account", "wallet", "credit limit", and "transaction" only become clear after multiple iterations with real customers. Defining service boundaries before understanding the domain is the mistake Martin Fowler describes with surgical precision in MonolithFirst: you distribute complexity before understanding it, and refactoring boundaries between microservices is orders of magnitude more costly than moving modules within a monolith.

Team size and capacity. With 4–7 engineers, every engineering hour is scarce. Microservices introduce immediate and non-trivial operational overhead: per-service CI/CD pipelines, service discovery, distributed observability (correlated traces, log aggregation, per-service metrics), API versioning management, eventual consistency between services, and circuit breaking. This overhead exists regardless of whether the system is in production or not. In small teams, this cost is paid entirely before any business feature is delivered.

Blast radius and failure isolation. Here microservices have a real advantage: a failure in the notification service does not bring down payment processing. In a monolith, a bug in low-criticality code can — if poorly isolated — affect the entire process. This argument is legitimate, but its strength depends directly on how well the monolith is structured internally. A modular monolith with clear separation of responsibilities, per-module error handling, and internal circuit breakers significantly reduces this risk without requiring distributed infrastructure.

Regulatory requirements. PCI-DSS and Bacen regulations require cardholder data segregation, access auditability, and eventually network isolation for components that process sensitive data. This creates real pressure for separation — but that separation can be addressed with module isolation + encryption at rest and in transit + granular access controls within a monolith, at least in the initial stage. Physical separation into distinct services can be deferred without violating regulatory requirements as long as compensating controls are in place.

Options Considered

Option A — Microservices from day one

Pros
  • Blast radius limited per service from the first deploy
  • Independent deployment per business domain
  • Granular scalability (scale only what needs it)
  • Alignment with future teams (each team owns a service)
Cons
  • Immediate operational overhead disproportionate to team size
  • Domain boundaries still unknown — high risk of wrong cuts
  • Network latency and eventual consistency introduce hard-to-reproduce bugs
  • High infrastructure cost before any revenue
  • Distributed observability requires mature tooling (Jaeger/X-Ray, centralized logging) from day 1

Rejected for initial phase. Operational cost and risk of wrong cuts outweigh the benefits in a 4–7 person team with an unconsolidated domain.

Option B — Modular monolith with planned extraction

Pros
  • Development speed: no network overhead, service mesh, or multiple pipelines
  • Cheap boundary refactoring while the domain is still being discovered
  • Simple observability at the start (single process, structured logs, single trace context)
  • Well-defined modules allow surgical extraction when the team grows
  • Significantly lower initial infrastructure cost
Cons
  • Larger blast radius if modules are not disciplinedly isolated
  • Risk of accidental coupling growing over time ('big ball of mud' without governance)
  • Any change requires rebuild and redeploy of the full artifact
  • Granular scalability not available without prior extraction

Accepted. Maximizes validation speed and reduces risk of wrong cuts, with a clear path for incremental extraction.

Option C — Unstructured monolith (CRUD app)

Pros
  • Maximum initial prototyping speed
Cons
  • No separation of concerns: impossible to extract services later without rewrite
  • Structural technical debt from the first commit
  • Incompatible with auditability and regulatory data segregation requirements

Rejected. Initial speed does not compensate for structural debt and regulatory incompatibility.

Recorded Decision

Accepted
Context

Team of 4–7 engineers building a greenfield fintech with payments and KYC domain still being discovered. 6–9 month timeline to first paying customer. Regulatory requirements (Bacen, PCI-DSS) require segregation and auditability, but do not impose a specific service topology at the initial stage.

Decision

Adopt a modular monolith as the initial architecture, with modules explicitly bounded by domain (Payments, Accounts, KYC, Notifications, Auth), internal interfaces formalized via ports/adapters (Hexagonal Architecture), a single database with separate schemas per module, and pre-defined criteria for service extraction. Extraction of a module into a microservice will only be initiated when: (1) the bounded context has been stable for at least two quarters, (2) the team grows beyond 10 engineers, or (3) a specific module presents scale or blast radius requirements incompatible with the monolith.

Consequences
  • POSITIVE: Team can focus on delivering business features without distributed infrastructure overhead in the first months.
  • POSITIVE: Domain boundaries can be refactored at low cost while the business is still being discovered.
  • POSITIVE: Observability, debugging, and rollback are significantly simpler in a single process.
  • NEGATIVE: Any module deployment requires redeployment of the full artifact — mitigated with feature flags and blue/green deployment at the infrastructure level.
  • NEGATIVE: Blast radius of an unhandled failure affects all modules — mitigated with isolated modules, internal bulkheads, and per-layer error handling.
  • RISK: Without active boundary governance, the monolith can degenerate into a big ball of mud. Mitigation: per-module code ownership, automated architecture tests (ArchUnit or equivalent) that prevent unauthorized cross-module dependencies.

Why Wrong Boundaries Are Worse Than No Boundaries

The most common argument for microservices from the start is that "it is easier to start right than to refactor later". This argument has an implicit premise that is rarely examined: that you know what "right" is before having real customers.

In practice, the bounded contexts of a fintech emerge from real usage. What appears to be an "accounts" domain on day 1 frequently splits into "checking account", "payment account", "investment account", and "revolving credit limit" after six months of product feedback. If you have already built four separate microservices with their own databases, APIs, and pipelines, that refactoring carries enormous coordination cost: distributed data migrations, API versioning, coordinated maintenance windows across services, and risk of transactional inconsistency during the transition.

In a well-structured modular monolith, the same refactoring is a code movement operation within a single repository, with a single database to migrate and a single artifact to redeploy. The cost is a fraction.

This does not mean microservices are wrong — it means they are a solution to an organizational and technical scale problem that most greenfield fintechs do not yet have. Conway's Law is real: your architecture will reflect your team's communication structure. With 5 engineers, the optimal communication structure is a single cohesive team working on a single artifact with clear internal boundaries. Artificially distributing that team into "service teams" before having the scale for it is a way to create coordination overhead without the corresponding benefits.

Fowler's MonolithFirst pattern is not a recommendation to never use microservices. It is a recommendation to earn the right to use them: understand your domain well enough to cut the correct boundaries, and have the team and operational maturity to pay the cost of operating them.

Target Architecture: Modular Monolith on AWS

Diagram of the resulting architecture from the decision: modular monolith running on ECS Fargate, with explicitly bounded internal modules, a single database with separate schemas, and identified future extraction points. The internal event bus (in-process) can be externalized to EventBridge when the first module is extracted.

🌐 Edge / Clients
  • Mobile App · iOS / Android
  • Partner API · B2B Integrations
🔒 Security & Edge — AWS
  • AWS WAF · + Shield
  • API Gateway · REST / JWT Auth
  • Amazon Cognito · Identity & Tokens
⚙️ Compute — ECS Fargate
  • Modular Monolith · Single deployable artifact
  • Module: Auth · ports/adapters
  • Module: Accounts · ports/adapters
  • Module: Payments · ports/adapters
  • Module: KYC · ports/adapters
  • Module: Notifications · ports/adapters
  • In-Process Event Bus · (→ EventBridge on extraction)
🗄️ Data — RDS + Cache
  • RDS PostgreSQL · Multi-AZ
  • Schema: auth
  • Schema: accounts
  • Schema: payments
  • Schema: kyc · (PCI isolated)
  • ElastiCache Redis · Session / Rate limit
📊 Observability
  • CloudWatch · Logs + Metrics
  • AWS X-Ray · Tracing
🚀 CI/CD
  • CodePipeline · + CodeBuild
  • ECR · Container Registry
🔗 External Integrations
  • Pix / DICT · Bacen
  • KYC Provider · (Serpro / Idwall)

Boundary Governance: What Separates a Modular Monolith from a Big Ball of Mud

The decision for a modular monolith only has value if it comes with explicit architectural discipline. Without it, the initial speed turns into structural debt in 12–18 months, and future extraction becomes as costly as premature distribution would have been.

Schema separation in the database. Each module accesses exclusively its own schema in PostgreSQL. Cross-schema queries are prohibited via code review and, ideally, via a separate database user per module (each module has a PostgreSQL role with GRANT only on its schema). This simulates the data isolation we would have with separate databases in microservices, without the cost of managing multiple RDS instances.

Formalized internal interfaces. Modules do not directly call classes or functions from other modules. Communication is done exclusively via public interfaces defined in the ports layer (Hexagonal Architecture). This ensures that, at extraction time, the interface already exists and can be converted to an HTTP call or event message without structural refactoring.

Automated architecture tests. Tools like ArchUnit (Java) or TypeScript equivalents (dependency-cruiser) are configured in the CI pipeline to fail the build if an unauthorized cross-module dependency is introduced. This is the only way to ensure that boundary discipline is maintained as the team grows and feature pressure increases.

Internal event bus as extraction preparation. Asynchronous communication between modules (e.g., Payments publishes PaymentCompleted, Notifications consumes and sends the receipt) is done via an in-process event bus. The interface of this bus is identical to what would be used with EventBridge or SNS/SQS. When the Notifications module is extracted, the only change is replacing the bus implementation with a real call to EventBridge — the producer and consumer code remains unchanged.

Documented and reviewed extraction criteria. The decision to extract a module should not be made under momentary pressure or because "we are already here". The criteria are defined now: bounded context stable for two quarters, team above 10 engineers, or specific technical requirement (differentiated scale, unacceptable blast radius, regulatory network isolation requirement). These criteria are reviewed quarterly at the architecture meeting.

Operational Comparison: Modular Monolith vs Microservices — Initial Phase

DimensionModular MonolithMicroservices
CI/CD Pipelines1 pipelineN pipelines (1 per service)
Initial observabilityStructured logs + CloudWatch sufficientRequires distributed tracing, log correlation, service map from day 1
Infra cost (estimated, MVP phase)~$300–600/month (ECS + RDS Multi-AZ + cache)~$1,200–2,500/month (multiple ECS tasks, multiple RDS, ALBs, service mesh)
Boundary refactoringLow cost — moves code within the repoHigh cost — data migration, API versioning, cross-team coordination
Failure blast radiusLarger, but mitigable with internal bulkheadsSmaller per service, but network failures introduce new failure modes
Time to first production deployWeeksMonths (distributed infra setup)

Architectural Evolution Roadmap

  1. 1

    Phase 1 — 0 to 9 months: MVP with Modular Monolith

    Build the monolith with explicit modules (Auth, Accounts, Payments, KYC, Notifications), separate schemas in PostgreSQL, in-process event bus, and architecture tests in CI. Deploy on ECS Fargate with blue/green. Full focus on product validation.

  2. 2

    Phase 2 — 9 to 18 months: Domain Stabilization and Team Growth

    Review bounded contexts based on real feedback. Evaluate extraction criteria. If the team grows beyond 10 engineers or a module presents a specific requirement, begin extraction of the first candidate (likely Notifications — low coupling, latency-tolerant, no strong consistency requirement).

  3. 3

    Phase 3 — 18+ months: Evidence-Based Incremental Extraction

    Extract modules into microservices only when documented criteria are met. Each extraction reuses the existing ports/adapters interface. The internal event bus is replaced by EventBridge. The separate schema is promoted to an independent database with controlled migration.

AWS Well-Architected Framework Assessment

Security

Separate schemas per module with distinct PostgreSQL roles. WAF + API Gateway with JWT authentication via Cognito. KYC data in isolated schema with additional encryption at rest (RDS encryption + KMS CMK). Secrets managed via AWS Secrets Manager, never in environment variables or code.

Reliability

RDS Multi-AZ for automatic failover. ECS Fargate with health checks and auto-recovery. Blue/green deployment eliminates downtime on deploys. Internal circuit breakers per module prevent cascading failures. Automated RDS backup with 7-day retention.

Performance efficiency

ElastiCache Redis for sessions and rate limiting (avoids database round-trips on critical paths). Connection pooling at the application level (PgBouncer or equivalent). No network latency between modules — in-process calls. Horizontal scalability via ECS task scaling when needed.

Sustainability

Load consolidation in a single process reduces idle CPU consumption compared to multiple underutilized containers. Fargate Spot can be used for non-critical workloads (e.g., batch processing jobs). RDS with instances sized for real load, not theoretical peak.

FA
My Senior Take
Senior Solutions Architect

I have seen this movie many times. A team of 5 engineers, a product not yet validated, investor pressure to "scale", and the decision to go with microservices because "that is how the big fintechs do it". The result, invariably, is six months spent on infrastructure and zero on product. The inconvenient truth is that microservices are a solution to the problem of scaling teams, not scaling systems. You can scale a well-built monolith to volumes that most Brazilian fintechs will never reach. What you cannot easily scale is the development process when you have 50 engineers all touching the same code. That is the problem microservices solve — and it simply does not exist with 5 people. What I would do in this scenario: modular monolith from day 1, with boundary discipline being non-negotiable. Architecture tests in CI that fail the build if anyone imports directly from another module. Separate schemas in the database from the start — this costs zero and buys a lot in terms of preparation for future extraction and regulatory isolation. In-process event bus with an interface identical to EventBridge. And most importantly: document the extraction criteria now, when there is no pressure. Because when the team grows to 12 people and the Payments module is running 50 deploys per day, the pressure to extract will be enormous — and you will want to have rational documented criteria, not make the decision in the heat of the moment. Fowler's MonolithFirst pattern is not nostalgia. It is pragmatic software engineering applied to the reality of small teams and domains under discovery

Verdict

For a greenfield fintech with a team of 4–7 engineers and a domain still under discovery, the modular monolith is the correct architectural decision — not for lack of ambition, but for excess of pragmatism. The choice is not between monolith and microservices. It is between distributing complexity you understand or distributing complexity you are still learning. The second option creates systems that nobody can fully reason about, with wrong boundaries carved into API contracts and separate database schemas. The modular monolith, built with boundary discipline, separate schemas, in-process event bus, and automated architecture tests, delivers the essential benefits that matter now — speed, operational simplicity, refactorability — while preserving the path for incremental extraction when real conditions justify it. The decision of when to extract is as important as the decision not to extract prematurely. Document the criteria. Review them quarterly. And when the moment comes, you will have modules with clean interfaces, isolated schemas, and an event bus ready to be externalized — not a spaghetti that needs to be rewritten before being distributed.

#architecture#fintech#monolith#microservices#adr#greenfield#modular#trade-offs
Share:
Written with AI assistance from the public case and my architect's reading.