Building Complexus: How I am Building a Modern Enterprise Frontend Architecture with Next.js and Turborepo
Introduction: Crafting a Modern Project Management System Building a sophisticated, feature-rich application like a project management system demands more than just choosing a popular framework. It requires a deliberate architectural approach to handle complexity, ensure maintainability, and enable efficient scaling. This article chronicles the journey of architecting the frontend for Complexus (complexus.app), a project management system designed for modern workflows. Drawing inspiration from the principles outlined in my previous article on structuring enterprise frontends, this article dives deeper into the specific choices made for Complexus. We'll explore how a combination of Turborepo, a custom UI library, Domain-Driven Design (DDD) principles adapted for the frontend, Next.js, NextAuth.js v5 for shared authentication, and a robust tooling suite come together to create a scalable and maintainable frontend architecture. Whether you're building your own complex application or simply curious about modern frontend practices, this detailed exploration of the Complexus frontend architecture aims to provide practical insights and patterns. The Foundation: Turborepo Monorepo for Structure and Speed For a project like Complexus, involving multiple distinct applications (the core project management app, a marketing site, documentation) and shared libraries, a monorepo structure offers significant advantages over managing multiple separate repositories. It simplifies dependency management, streamlines code sharing, enables atomic commits across related parts, and enforces consistency. I chose turborepo as the build system and monorepo manager due to its focus on performance. Its key strengths lie in: Intelligent Caching: Turborepo caches the output of tasks (builds, tests, linting), ensuring work isn't repeated unnecessarily, both locally and potentially remotely (Remote Caching). This dramatically speeds up CI/CD pipelines and local development loops. Efficient Task Orchestration: It understands the dependency graph between packages and tasks, running them in parallel whenever possible and in the correct order. Turbopack Integration (for Development): Leveraging Turbopack with Next.js during development (next --turbopack) provides near-instantaneous updates, further enhancing the developer experience. Complexus Monorepo Structure The Complexus monorepo follows a standard Turborepo structure, separating applications from shared packages complexus-monorepo/ ├── apps/ │ ├── projects/ # The core Next.js project management system │ ├── landing/ # The Next.js landing page │ └── docs/ # Documentation site (using Nextra) ├── packages/ │ ├── ui/ # Custom UI component library │ ├── icons/ # Shared SVG icons package │ ├── lib/ # Shared utilities (e.g., cn function) │ ├── tailwind-config/ # Shared Tailwind CSS configuration │ ├── eslint-config/ # Shared ESLint configuration preset │ └── tsconfig/ # Shared TypeScript base configurations ├── package.json # Root dependencies and scripts ├── pnpm-workspace.yaml # Defines workspace locations (using pnpm) └── turbo.json # Turborepo pipeline configuration This clear separation isolates deployable units (apps/) from reusable code (packages/), promoting modularity and making the codebase easier to navigate and maintain. Streamlining Development: Shared Packages (packages/) The packages/ directory is the heart of code sharing and consistency within the Complexus monorepo. ui: Contains the custom-built UI component library. In Atomic design these are the atoms that can be used for build other features: icons: Manages and exports SVG icons used across the applications. lib: Shared utility functions. Includes cn, a function combining clsx and tailwind-merge to handle Tailwind class conflicts. tailwind-config: Base Tailwind CSS theme used across all apps/packages an the ui package. eslint-config: Base ESLint configuration preset for consistent linting rules. tsconfig: Base tsconfig.json files that other packages extend. Using these shared packages ensures consistency, reduces boilerplate, and simplifies dependency and configuration updates. Structuring Logic: Domain-Driven Modules in apps/projects While the UI library provides the visual building blocks, structuring the application logic within the main projects app (apps/projects/) requires a different approach. Simply grouping files by type (e.g., top-level hooks/, services/) quickly becomes unmanageable in a complex application. Complexus adopts principles from Domain-Driven Design (DDD), specifically organizing code by feature or domain within a modules/ directory. Each subdirectory within modules/ represents a distinct business capability or feature set of the project management system. apps/projects/src/ ├ app/

Introduction: Crafting a Modern Project Management System
Building a sophisticated, feature-rich application like a project management system demands more than just choosing a popular framework. It requires a deliberate architectural approach to handle complexity, ensure maintainability, and enable efficient scaling. This article chronicles the journey of architecting the frontend for Complexus (complexus.app), a project management system designed for modern workflows.
Drawing inspiration from the principles outlined in my previous article on structuring enterprise frontends, this article dives deeper into the specific choices made for Complexus. We'll explore how a combination of Turborepo, a custom UI library, Domain-Driven Design (DDD) principles adapted for the frontend, Next.js, NextAuth.js v5 for shared authentication, and a robust tooling suite come together to create a scalable and maintainable frontend architecture.
Whether you're building your own complex application or simply curious about modern frontend practices, this detailed exploration of the Complexus frontend architecture aims to provide practical insights and patterns.
The Foundation: Turborepo Monorepo for Structure and Speed
For a project like Complexus, involving multiple distinct applications (the core project management app, a marketing site, documentation) and shared libraries, a monorepo structure offers significant advantages over managing multiple separate repositories. It simplifies dependency management, streamlines code sharing, enables atomic commits across related parts, and enforces consistency.
I chose turborepo as the build system and monorepo manager due to its focus on performance. Its key strengths lie in:
- Intelligent Caching: Turborepo caches the output of tasks (builds, tests, linting), ensuring work isn't repeated unnecessarily, both locally and potentially remotely (Remote Caching). This dramatically speeds up CI/CD pipelines and local development loops.
- Efficient Task Orchestration: It understands the dependency graph between packages and tasks, running them in parallel whenever possible and in the correct order.
-
Turbopack Integration (for Development): Leveraging Turbopack with Next.js during development (
next --turbopack
) provides near-instantaneous updates, further enhancing the developer experience.
Complexus Monorepo Structure
The Complexus monorepo follows a standard Turborepo structure, separating applications from shared packages
complexus-monorepo/
├── apps/
│ ├── projects/ # The core Next.js project management system
│ ├── landing/ # The Next.js landing page
│ └── docs/ # Documentation site (using Nextra)
├── packages/
│ ├── ui/ # Custom UI component library
│ ├── icons/ # Shared SVG icons package
│ ├── lib/ # Shared utilities (e.g., cn function)
│ ├── tailwind-config/ # Shared Tailwind CSS configuration
│ ├── eslint-config/ # Shared ESLint configuration preset
│ └── tsconfig/ # Shared TypeScript base configurations
├── package.json # Root dependencies and scripts
├── pnpm-workspace.yaml # Defines workspace locations (using pnpm)
└── turbo.json # Turborepo pipeline configuration
This clear separation isolates deployable units (apps/
) from reusable code (packages/
), promoting modularity and making the codebase easier to navigate and maintain.
Streamlining Development: Shared Packages (packages/
)
The packages/
directory is the heart of code sharing and consistency within the Complexus monorepo.
-
ui
: Contains the custom-built UI component library. In Atomic design these are the atoms that can be used for build other features: -
icons
: Manages and exports SVG icons used across the applications. -
lib
: Shared utility functions. Includescn
, a function combiningclsx
andtailwind-merge
to handle Tailwind class conflicts. -
tailwind-config
: Base Tailwind CSS theme used across all apps/packages an theui
package. -
eslint-config
: Base ESLint configuration preset for consistent linting rules. -
tsconfig
: Basetsconfig.json
files that other packages extend.
Using these shared packages ensures consistency, reduces boilerplate, and simplifies dependency and configuration updates.
Structuring Logic: Domain-Driven Modules in apps/projects
While the UI library provides the visual building blocks, structuring the application logic within the main projects app (apps/projects/
) requires a different approach. Simply grouping files by type (e.g., top-level hooks/
, services/
) quickly becomes unmanageable in a complex application.
Complexus adopts principles from Domain-Driven Design (DDD)
, specifically organizing code by feature or domain within a modules/
directory. Each subdirectory within modules/
represents a distinct business capability or feature set of the project management system.
apps/projects/src/
├ app/ # Next.js App Router (layouts, pages, route handlers)
├ components/ # Truly global components (rarely needed)
├ lib/ # App-specific core utilities, API client, global context
├modules/ # Domain/Feature-specific code
│ ├sprints/ # Example: Sprints feature/domain
│ │ ├ components/ # UI components specific to Sprints (using `ui`)
│ │ ├ hooks/ # React hooks for Sprint-related logic/state
│ │ ├ queries/ # Data fetching queries (e.g., using React Query)
│ │ ├ mutations/ # Data mutation logic
│ │ ├ lib/ # Utility functions specific to Sprints
│ │ └ types/ # TypeScript types specific to Sprints
│ ├ tasks/ # Example: Tasks feature/domain
│ │ └..
│ ├ settings/ # Example: Project Settings feature/domain
│ │ └...
│ └──... # Other features/domains
├── styles/ # Global styles
└──...
Advantages of Feature-Based Structure
- Cohesion: Related code lives together.
- Modularity: Features are self-contained.
- Scalability: Easy to add new modules.
- Team Collaboration: Teams can own modules.
Within each module, React hooks (hooks/
) are heavily used to encapsulate logic, state management, and interactions with data fetching libraries (like React Query, managing queries/
and mutations/
). Components specific to the module (components/
) compose reusable elements from ui
to build the feature's interface.
Seamless Access: Shared Authentication Across Subdomains
Complexus uses subdomains (e.g., workspace-a.complexus.app
, workspace-b.complexus.app
). A critical requirement is that users authenticating on any valid subdomain (*.complexus.app
) should remain authenticated across all others without needing to log in again.
This is achieved using NextAuth.js v5 and careful cookie configuration. The core challenge is ensuring the session cookie set by NextAuth.js is accessible across all relevant subdomains.
The key lies in setting the domain attribute for the session cookie within the NextAuth configuration. Below is an example config:
cookies: {
sessionToken: {
name: "__Secure-next-auth.session-token",
options: {
httpOnly: true,
sameSite: "lax",
path: "/",
// CRUCIAL: Set domain for cross-subdomain access
domain: ".example.com",
secure: true
},
},
This configuration, combined with hosting on a platform like Vercel which handles subdomain routing, allows Complexus to provide a seamless authentication experience across its different workspaces.
Documenting with Nextra
To ensure Complexus is well-understood by users, a dedicated documentation site is maintained within the apps/docs
application.
Nextra integrates smoothly with Next.js and allows writing documentation using Markdown (MDX), making it easy to embed React components directly into the documentation pages. It provides excellent defaults for documentation sites, including sidebar navigation, search, and theming.
Conclusion: Building for Complexity and Scale
Architecting the frontend for Complexus (complexus.app)[www.complexus.app] involved a series of deliberate choices aimed at managing complexity, fostering consistency, and ensuring the application can scale effectively. The combination of:
- A Turborepo monorepo for structure and build performance.
- Shared packages for UI components, configuration, and utilities.
- Domain-Driven Design principles guiding the feature-based module structure in the main application.
- NextAuth.js v5 configured for seamless cross-subdomain authentication.
- Nextra for comprehensive documentation.
- A consistent tooling suite (TypeScript, ESLint, Tailwind).
This architecture prioritizes developer experience through fast builds (Turborepo, Turbopack) and clear organization, while ensuring the end product is maintainable and scalable. While this specific setup is tailored for Complexus, the underlying principles of modularity, clear boundaries, shared foundations, and efficient tooling are applicable to many large-scale frontend projects.