Go BackDec 12, 2025
Frontend System Design & Architecture — DDD, FSD, and My Hybrid Approach

Frontend System Design & Architecture — DDD, FSD, and My Hybrid Approach

Every frontend engineer eventually hits the moment when simple folder structures like components/ and utils/ stop being enough. As products grow, features expand, and teams scale, architecture becomes the difference between a codebase that feels like a well-organized city… and one that feels like a maze built blindfolded.

DDD - Domain-Driven Design

DDD focuses on business domains, not UI layers. It works best when the product has deep business logic: loans, payments, transactions, identity, roles, etc.

Pros:

• Strong boundaries between domains

• Great for large, complex systems

• Business rules stay organized

Cons:

• Heavy for frontends

• Requires discipline

• Unintuitive for typical React workflows

FSD - Feature-Sliced Design

FSD organizes code by features and shared layers. It shines when the UI is large, interactive, and constantly growing.

Pros:

• Natural fit for React & Next.js

• Features are isolated (components + hooks + services)

• Predictable structure

• Easy onboarding for new developers

Cons:

• Too much overhead for very small apps

• Doesn’t model complex business domains as well as DDD

Why I Use a Hybrid Architecture

In my projects, small to medium, neither pure DDD nor pure FSD fits perfectly. So instead of picking one, I built my own hybrid architecture, inspired by both. Because, DDD is too heavy and backend-ish, FSD is great but sometimes too UI-focused, if using NextJS, it already forces a route-driven boundary system (app/route/...), and enterprise apps require mixing UI architecture + domain boundaries.

My hybrid approach balances both worlds. As per route, it has their own components, hooks, styles, constants, and etc. This is pure FSD mindset. But, inside services, libs or Nextjs API routes (for example) I group logic by domain boundaries, following DDD principles.

Shared UI components, utils, and configs remain in the root layer, ensuring reusability across the app.

You can play with the code below:
Nextjs Project Structure
public
src
middleware.ts
.huskyrc
.gitignore
.prettierrc
{}
package.json
README.md
{}
tsconfig.json
...
File tree credits to @jatin-yadav05

I prefer my own hybrid approach for reasons, like -clear boundaries, localized features, shared common modules, predictable structure, and zero useless abstraction.

However, my approach has their own limitations, like - other developers may need deeper layering rules, not strict enough for complex DDD workflows, folder structure grows quickly in large projects, might require developer discipline without a formal system, etc.

Architecture is not one-size-fits-all. The best approach depends on your team, product, and context. The key is to stay flexible, iterate, and always prioritize maintainability and clarity.