Photograph from Mojtaba Roozegar’s original personal gallery
Personal gallery photograph used as an editorial illustration, not evidence of a business partnership.
Article

Website Architecture: Modular Monoliths, Domain Boundaries and ADRs

An advanced guide to bounded contexts, request lifecycles, deployable units, dependency direction, architecture decisions and measurable trade-offs.

Architecture is a set of constraints, not a framework slogan
A professional website architecture begins by naming workload, security boundary, data ownership, failure tolerance and operational budget. A content journal, multi-vendor marketplace and payment ledger differ in consistency requirements even when they share Django and PostgreSQL. Write non-functional requirements as measurable envelopes: p95 request latency under an agreed synthetic load, maximum permitted data loss, deployment recovery time and authorization assumptions. If the constraints are not explicit, a choice between monolith and microservices cannot be evaluated. Domain boundaries matter more than the number of repositories.

Define cohesive bounded contexts
Decompose by business capability rather than by arbitrary technical folders. Catalog, identity, editorial workflow, fulfillment and billing may use separate application modules, each with an explicit owner for state transitions. For example, a product may be visible in Catalog while the transaction ledger remains under Billing; Catalog should not write settlement status simply because both models live in the same database. Publish cross-context interfaces with typed inputs, versioned contracts and clear failure behavior. Avoid circular imports and prevent presentation layers from directly mutating records they do not own.

Dependency direction and transaction boundaries
Use a layered flow: HTTP adapter validates request shape; application service authorizes the operation; domain logic enforces invariants; repository or ORM adapter persists state; response assembler returns a limited representation. A unit of work can be a database transaction when all invariants share one datastore. Cross-database or external payment workflows require explicit state machines and compensating actions, not an imaginary global ACID transaction. Keeping dependencies directed inward makes high-value rules testable without the framework server or a real payment provider.

Modular monolith versus distributed services
A modular monolith offers local calls, one deployable artifact and simpler consistency while still allowing disciplined boundaries. Split a service only when independent scaling, compliance, team ownership or failure containment justify the extra cost of network calls, identity propagation, monitoring and schema evolution. Distributed systems introduce partial failure: one service may commit while another times out. A sensible architecture decision record documents context, alternatives, decision, assumptions, consequences and a reversal trigger, rather than merely naming a fashionable style.

Plan the read and write paths separately
Public website requests often read published content many times for every edit. Model content publishing as a controlled write operation, then cache public representations with appropriate invalidation. Private account pages must not inherit public caching rules. For a request, diagram TLS termination, reverse proxy, application process, database query, template rendering, compression and client hydration. Measure each segment separately; a slow site may be caused by database queries, third-party scripts or queue contention rather than insufficient CPU. Request traces should use correlation identifiers without leaking private payloads.

Record architecture decisions as testable hypotheses
An ADR for server-side rendering should include the crawlability requirement, initial HTML strategy, expected interaction model, fallback when JavaScript fails and performance measurements that would invalidate the decision. A data-store ADR should specify read patterns, transactions, backup and migration policy. Review consequences after real traffic exists; architecture is not an eternal decree. Use a small scorecard covering maintainability, deployability, security, latency and recovery. Each metric should have a named owner, measurement procedure and revision date.

Implementation checklist and common failure modes
Before building pages, define route ownership, configuration boundaries, secrets management, database migration rules, asset lifecycle and production rollback. Add contract tests between modules and forbid dependency violations with lint or import tests. Common failures include allowing every view to perform writes, duplicating price calculations in templates, coupling publication to checkout, and mixing demonstration data with real customers. A good outcome is a deployable system where a faulty catalog enhancement cannot corrupt billing or replace an unrelated site. The architecture should make the safe path the easiest path.

Further technical reading
Django deployment checklist: https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ ; OWASP ASVS 5.0: https://owasp.org/www-project-application-security-verification-standard/ . These are external standards and references, not evidence that this specific website has achieved their requirements.

Editorial disclosure: Original advanced educational content by Mojtaba Roozegar. Examples are illustrative, not an audit of a particular production deployment or a guarantee of outcomes. Relevant external technical references are linked within each article.

← Back to Articles