System Design Space
Knowledge graphSettings

Updated: May 21, 2026 at 9:00 AM

Frontend Application Architecture: App Shell, Feature Modules, and Shared Kernel

easy

How to structure frontend as a modular system: app shell, feature modules, shared kernel, import boundaries, public module APIs, and team ownership.

Frontend application architecture becomes important the moment the codebase no longer fits in one team’s head and every change starts touching routing, shared utilities, and neighboring features. That is where the app shell, feature modules, and strict rules around the shared kernel become necessary.

The practical value of the chapter is that it separates product flow from platform structure. Good frontend modularity speeds delivery not because folders look cleaner, but because ownership, import boundaries, and public module APIs become predictable.

For architecture reviews, it is a strong entry point into modular-monolith frontend thinking: how to keep the shell thin, stop the shared layer from expanding, and evolve the application without jumping into micro-frontends too early.

Practical value of this chapter

Design in practice

Turn frontend application boundaries into decisions about the app shell, feature modules, shared kernel, and import direction.

Decision quality

Evaluate the architecture through independent change, transparent dependencies, screen-load speed, and clear public module APIs.

Interview articulation

Structure answers as domain boundaries, shared frame, import rules, source of truth, and an evolution path without premature micro-frontends.

Trade-off framing

Make the cost of the shared kernel explicit: reuse convenience, blast radius, module ownership, and long-term platform-layer maintenance.

Context

Why Frontend Architecture Matters

This chapter turns the high-level frontend architecture overview into a real application structure: app shell, feature modules, and shared kernel.

Читать обзор

In a complex frontend product, application architecture defines not only code structure, but also how independently teams can move, how quickly screens load, and where public contracts between interface areas actually live.

A useful rule is to design module boundaries before debating a state library or framework pattern. Boundaries make it clear what is truly shared and what simply has not been decomposed into domains yet.

This chapter looks at frontend architecture through the app shell, feature modules, shared kernel, bounded contexts, module boundaries, dependency rules, public entrypoints, source of truth, and the path toward micro-frontends without premature complexity.

Architectural frame

The app shell owns the frame, not business logic

The app shell carries navigation, layout, auth session, telemetry wiring, and platform integration points. Business flows should not turn it into a shared monolith.

Feature modules follow product boundaries

Catalog, checkout, billing, editor, or reporting should have their own screens, data loading paths, and internal UI primitives instead of constantly reaching into a global shared layer.

The shared kernel stays small and expensive to change

Design tokens, routing primitives, the base auth session, telemetry adapters, and a few platform utilities belong there. Everything else starts local to a domain.

Public contracts matter more than convenient imports

Each module needs public entrypoints, a versionable module API, and clear ownership rules. Otherwise the app becomes a tightly coupled graph of accidental dependencies.

Related

Decomposition Strategies

A frontend app scales better when module boundaries match product bounded contexts.

Open chapter

Boundary rules

Imports only flow downward through the architecture

A feature module may depend on the shared kernel, but not directly on a neighboring module. Cross-domain scenarios go through a public contract or orchestration layer.

UI components split into local and platform primitives

Buttons, modal shells, and token-aware typography belong to the frontend platform. Domain widgets like pricing tables or document toolbars should not leak into the shared kernel too early.

Routing is architecture, not just developer experience

The route tree should reflect ownership and lazy-loading boundaries. Nested layouts and loaders should not accidentally pull the whole client runtime for every module.

Critical cross-cutting concerns connect consistently

Telemetry, error boundaries, auth guards, and feature-flag evaluation need one integration pattern so debugging and staged rollout do not depend on a local team style.

Team operating model

  • Each feature module has an owning team, a public module API, and rules for deprecating older contracts.
  • The shared kernel goes through stricter review than local domain code because its blast radius is much higher.
  • Architectural dependencies are captured in ADRs or import policies, not in informal agreements.
  • Refactoring starts with clearer module boundaries, not with an early jump to micro-frontends.

Practical quality bar

Independent change

A new feature can be added inside one domain without a cascade of edits across neighboring modules and shared utilities.

Transparent dependencies

The dependency graph and route tree make it clear where the source of truth lives and who owns each public entrypoint.

Controlled shared kernel

The common layer stays small, versionable, and rarely changed instead of becoming a dumping ground for everything reusable.

Related chapters

Enable tracking in Settings