Skip to content
IH
Back to work

Enterprise E-commerce · Full-Stack · Cloud

Enterprise Commerce Platform

A scalable commerce platform designed around modular business capabilities, secure identity management and reliable cloud deployment.

Role
Architecture, full-stack engineering, delivery
Timeline
2024 — Present
Stack
Next.jsTypeScriptJavaSpring BootPostgreSQLRedisAWSTerraform
iroshahewage.dev/preview
Overview

Context, challenge and the part I played.

Context

A multi-vendor commerce platform needed a foundation that several teams could extend over years, without the codebase collapsing into a single tangled service.

Challenge

Commerce systems accumulate features quickly. Catalogue, orders, payments, identity and notifications all want to reach into each other, and within a year that coupling makes every change expensive.

Goals

  • Keep business capabilities separable as the platform grows
  • Make identity and permissions a first-class part of the architecture
  • Give the team a deployment path that is repeatable and reviewable
  • Leave room for future channels without rewriting the core

My role

I set the architecture, defined the module boundaries and the shared conventions, built across the front-end and backend, and provisioned the cloud environment.

Approach

From interface down to infrastructure.

Each layer, and the reasoning that shaped it.

Architecture

A modular monolith: one deployable unit, several strictly separated modules. Each module owns its data and exposes a named interface; cross-module reads go through that interface rather than a shared database join. This keeps the operational simplicity of a single service while preserving the option to extract a module later.

Front-end

Next.js with the App Router and TypeScript throughout. Server components handle data fetching so the client bundle stays small; a shared component library keeps the customer storefront and the admin console visually consistent without duplicating logic.

Backend

Java and Spring Boot, with domain types instead of primitives for identifiers and money. Application services stay thin, business rules live in the domain layer, and every exception path is modelled explicitly rather than thrown as a generic runtime error.

Database

PostgreSQL with per-module migrations, so schema changes travel with the code that needs them. No foreign keys across module boundaries — references between aggregates are carried by identifier, which keeps the boundary honest.

Cloud & deployment

Containerised services on AWS, provisioned with Terraform and released through GitHub Actions. Environments are described in code, so staging and production differ by configuration rather than by history.

Capabilities

What the system actually does.

  • Customer and administrative applications

    Two separate front-end experiences sharing one design system and one API contract.

  • Secure authentication and role management

    Server-side sessions, granular roles and permission checks enforced at the service boundary.

  • Product and order workflows

    Catalogue, pricing, cart and order lifecycle modelled as explicit states rather than flags.

  • Modular backend architecture

    Business capabilities isolated into modules with published interfaces and enforced boundaries.

  • Redis-backed platform services

    Session storage, caching and rate limiting handled by shared infrastructure services.

  • Automated cloud deployment

    Container images built and released through a pipeline with no manual server access.

  • Infrastructure as code

    Networking, compute and data services declared in Terraform and reviewed like application code.

Gallery

Interface and system views.

Placeholder frames — swap for real captures once they exist.

  • iroshahewage.dev/preview
    Storefront — product listing and filtering
  • admin · orders
    Admin console — order lifecycle view
  • Checkout on mobile
  • InterfaceIdentityCatalogueOrdersPostgreSQL · RedisAWS · Terraform · Containers
    Module boundaries and data ownership
Decisions

The trade-offs worth writing down.

Every architecture is a set of choices. These are the ones that mattered most, and why they went the way they did.

A modular monolith rather than microservices

Microservices would have added network boundaries, distributed transactions and an operations burden before there was a team to carry it. Enforcing module boundaries inside one deployable gives most of the separation benefit at a fraction of the cost, and leaves extraction available when the load or the team actually calls for it.

Server-side sessions instead of stateless tokens

For a web-first commerce product, sessions in Redis made revocation immediate and kept sensitive claims off the client. Tokens remain the right answer for machine clients, so the identity module was designed to issue both.

Typed identifiers everywhere

Passing raw strings for identifiers invites the kind of bug that only appears in production. Prefixed, typed identifiers make the wrong argument a compile error and make logs readable at a glance.

Outcome

Where it stands.

  • Created a maintainable foundation for a long-lived commerce platform
  • Improved separation of responsibilities between business capabilities
  • Designed for future scaling without committing to distributed complexity
  • Supported secure and structured platform development

Want the detail behind any of these decisions?

Start a conversation