Foundations · For Senior engineers, architects, CTOs
Recognise your architecture style before you secure it
Layered, modular monolith, hexagonal, microservices, event-driven or serverless: how to tell which one you really run, and where each style puts its trust boundaries.
A security checklist that ignores your architecture style checks the wrong places. Each style moves the boundaries somewhere else.
Why the style decides where security lives
An architecture style is a set of constraints on how a system is divided and how its parts talk. That sounds abstract until you ask a security question. Where is authorisation enforced? How does a user's identity reach the code that touches their data? If one part is compromised, what else can it call? The answers are different in a modular monolith, a set of microservices and a serverless application, even when all three implement the same product.
This is why generic checklists disappoint. "Use mutual TLS between services" means nothing in a single process, and "validate input at the controller" misses half the inputs of an event-driven system. Before you review a design, name its style, or more often its mix of styles, and let that tell you where to look.
Six styles and the signals that give them away
Most teams describe their architecture by what they intended. The code and infrastructure tell you what you actually built. These are the signals to look for, and what each style means for security.
Layered (n-tier)
Signals: one or a few deployables; folders such as controllers, services, repositories; calls flowing strictly downward to one database. Security: the boundary is the top layer. Everything below tends to trust it, so a single missing check in a controller exposes the whole stack, and internal callers such as batch jobs often bypass it.
Modular monolith
Signals: one deployable with clear module directories, each owning its tables or schema, and rules (build checks, architecture tests) that forbid imports across module internals. Security: module boundaries are logical, not enforced by the runtime. A bug or a compromised dependency anywhere runs with the whole process's identity, so data access per module must be enforced in code and, ideally, with separate database roles.
Hexagonal (ports and adapters)
Signals: a domain core with no framework imports, interfaces named as ports, and adapters for HTTP, queues, databases and third parties. Security: adapters are where untrusted input arrives and where secrets for external systems live. Validation and authentication belong in the driving adapters; the core can then enforce business rules on clean, typed commands.
Microservices
Signals: many independently deployed services, each with its own data store, an API gateway or service mesh, and a deployment pipeline per service. Security: every call between services crosses a network and an identity boundary. Each service must authenticate its callers and authorise the action itself; a flat network where any service can call any other undoes the isolation you paid for.
Event-driven
Signals: brokers, topics or streams, schema registries, consumers that react to events rather than answering requests. Security: every consumer has inputs that no API gateway saw. Who may publish to a topic is an authorisation decision, events need integrity and replay protection, and sensitive data in an event spreads to every subscriber, including future ones.
Serverless
Signals: function handlers, managed triggers (HTTP, queues, storage events, schedules) and a permission policy per function. Security: the platform handles patching and isolation, while you own the event sources and the permissions. The most common weakness is a function role far broader than its one job, multiplied across hundreds of functions.
The same domain, split two ways
The clearest way to see a style's effect is to draw the same domain both ways. Here orders and billing appear once as modules in one process and once as two services.
Neither version is more secure by default. The monolith has fewer boundaries to get wrong but weaker isolation between them; the services have stronger isolation but many more places where a missing check matters. What matters is that the controls match the style you actually run.
Read the diagram as text
- One process: Orders module, Billing module, Shared database
- Orders service: Orders
- Billing service: Billing, Billing database
- Gateway → Orders module: request
- Orders module → Billing module: function call
- Billing module → Shared database: app's role
- Gateway → Orders: request
- Orders → Billing: signed call
- Billing → Billing database: own role
Hybrids are the norm, so name the style per area
Very few production systems follow one style. A typical product has a modular core, a few services split out for scale or ownership, an event bus for integrations and a handful of functions for scheduled jobs. That is fine. Trouble starts when a review applies one style's assumptions to another's area: treating serverless handlers as if a gateway protected them, or assuming a module boundary stops a compromised dependency.
A practical habit is to label each area of the system with its dominant style and keep that label next to the architecture model. When someone proposes a change, the label tells reviewers which questions apply.
Questions to ask whatever the style
- Where does each kind of input arrive, and which component validates it first?
- How does the end user's identity reach the code that reads their data, and can that code verify it?
- Which identity does each deployable or function run as, and what can that identity reach?
- If one component were fully compromised, which data and which other components could it touch next?
- Where are the module, service or function boundaries enforced by the runtime, and where only by convention?
The answers usually show that the boundaries enforced by convention, an import rule, a naming scheme, a code review habit, are the ones worth reinforcing first.
Choosing or changing a style for security
Security is rarely a good reason on its own to move to microservices. Splitting is worth it when parts of the system genuinely need different treatment: much more sensitive data, a different risk tolerance, separate regulatory scope, or code you trust less, such as user-provided plug-ins. In those cases the extra boundary pays for itself.
Otherwise, a well-enforced modular monolith is easier to reason about, easier to audit and has fewer network paths to defend. Many experienced teams start there and extract services only when a boundary has proven itself. Whatever you choose, record it in an architecture decision record with the security reasoning, so the next team knows which boundaries were deliberate.
Sources
- Architecture styles
Microsoft Azure Architecture Center
- Microservices
James Lewis and Martin Fowler
- MonolithFirst
Martin Fowler
- Hexagonal architecture
Alistair Cockburn
- What do you mean by “Event-Driven”?
Martin Fowler
- Security Strategies for Microservices-based Application Systems (SP 800-204)
NIST
- Microservices Security Cheat Sheet
OWASP
- Security in AWS Lambda
Amazon Web Services