DEV Community

Cover image for From Pain Points to Practice: Monorepo Architecture in PawHaven
Aoda Zhang
Aoda Zhang

Posted on

From Pain Points to Practice: Monorepo Architecture in PawHaven

In modern full-stack projects, frontend, backend, shared libraries, and utility scripts are often tightly interwoven. As features grow and teams expand, using multiple independent repositories (polyrepo) can lead to several challenges:

  • Inconsistent versions of shared modules
  • Confusing imports and mismatched types
  • Difficulty keeping cross-project refactors consistent
  • Scattered and hard-to-maintain configuration

Against this backdrop, Monorepo—keeping multiple sub-projects in a single repository—becomes an attractive solution.

However, it’s a misconception that Monorepo fits all projects. In this article, we will cover:

  1. Suitable scenarios for Monorepo
  2. Why PawHaven chose Monorepo
  3. Architecture design and directory structure
  4. Configuration reuse practices

1. When is Monorepo Suitable?

Monorepo has clear boundaries for its effectiveness. The following scenarios benefit the most:

Scenario Reason
Multiple tightly-coupled sub-projects/modules Need to share types, DTOs, utilities, etc.
Frequent cross-module changes/refactoring One commit can update multiple packages simultaneously
Team coordination is manageable Frequent inter-module collaboration
Desire for unified CI/CD pipelines Shared pipelines, caching, and dependency management
Want to reuse configuration and toolchains Centralized management for ESLint, tsconfig, styles
Reduce version conflicts Shared dependencies are easier to align in one repo

Situations where Monorepo may not be suitable:

  • Small projects with simple module relationships
  • Modules with almost no shared logic
  • Significantly different tech stacks making unified config difficult
  • Distributed teams requiring strict permission separation

Challenges to be aware of:

As the repository grows, build, clone, and CI times may increase.

Access control, dependency boundaries, and tooling also become more complex.

Conclusion: Monorepo isn’t inherently “better” or “advanced”; it should be adopted after carefully weighing the benefits against the added complexity.

2. Why PawHaven Chose Monorepo

PawHaven is a full-stack project consisting of React frontend + NestJS backend + shared libraries. The decision to adopt Monorepo was driven by several factors:

1. Shared Types, DTOs, and Utilities

Interfaces, constants, and types shared between frontend and backend can be maintained in a single repository. TypeScript path aliases or workspace references avoid duplicate maintenance.

2. Unified Configuration Management

Centralized management of ESLint, tsconfig, Tailwind, etc., allows sub-packages to inherit configurations directly, maintaining consistency and reducing maintenance costs.

3. Cross-Package Changes in a Single Commit

When shared components, DTOs, or backend interfaces need synchronized updates, a single PR can handle all changes, ensuring compatibility.

4. Centralized CI/CD Pipeline

Build, test, and deployment processes are defined once at the repo level, with shared caching and toolchains, simplifying continuous integration.

5. Dependency Hoisting and Version Consistency

Using pnpm's hoist mechanism, all sub-packages share the same dependency versions, minimizing conflicts.

6. Single Entry Point Reduces Communication Overhead

For an open-source project, having one repository means developers, documentation, and promotional materials only need to focus on a single source, reducing coordination overhead across multiple repos.

3. Monorepo Structure Design & PawHaven Example

Design Principles

  • Separation of business and non-business code: Clearly distinguish deployable apps from shared libraries
  • Centralized configuration: ESLint, tsconfig, Tailwind maintained in independent config packages
  • Consistent structure: Each sub-package follows uniform directory conventions (src/, tsconfig.json, package.json)
  • Simplified imports: Use alias/workspace references instead of deep relative paths
  • Toolchain compatibility: Build, type-check, and lint processes cover cross-package paths

🧭 PawHaven Directory Structure

├─ pnpm-lock.yaml
├─ pnpm-workspace.yaml
├─ apps
│  ├─ backend
│  │  ├─ gateway
│  │  ├─ ms-auth
│  │  ├─ ms-document
│  │  └─ ms-pawhaven
│  └─ frontend
│     ├─ user
│     └─ admin
├─ packages
│  ├─ shared-frontend
│  ├─ i18n
│  ├─ shared-backend
│  ├─ theme
│  └─ ui
├─ libs
│  └─ configs
│     ├─ eslint-config
│     └─ tsconfig
├─ .vscode/
│  ├─ settings.json
│  └─ extensions.json

Enter fullscreen mode Exit fullscreen mode

Directory Explanation:

  • apps/: Independent frontend and backend business modules
  • packages/: Shared modules related to business (UI, theme, i18n, shared types)
  • libs/: Utility and configuration libraries, can be independently published

4. Unified Configuration (ESLint / tsconfig / Tailwind)

1️⃣ ESLint

Goal: Provide consistent rules for both Web and Node environments.

Implementation:

  • Centralized ESLint configs in libs/configs/eslint/
  • Separate configs for Web (eslint-config-web) and Node (eslint-config-node)
  • Sub-packages inherit and can override rules as needed

📦 Reference: PawHaven/libs/eslint

2️⃣ tsconfig

Goal: Unified TypeScript compilation options with support for multiple targets (Web/Node).

Implementation:

  • Base configuration in libs/configs/tsconfig/base.json with environment-specific extensions
  • Sub-packages only override output paths, rootDir, or other necessary options

📦 Reference: PawHaven/libs/tsconfig

3️⃣ Tailwind Design System

Goal: Ensure consistent styling across all frontend packages and UI components.

Implementation:

  • Centralized design tokens and global Tailwind config in packages/theme/
  • Shared theme between UI library and frontend apps
  • Ensure content scanning paths include shared component files

📦 Reference: PawHaven/packages/theme

5. Conclusion

PawHaven’s Monorepo architecture enables:

  • Centralized multi-module management and collaboration
  • Unified ESLint, TypeScript, and Tailwind configurations
  • Shared types and consistent dependencies between frontend and backend
  • Improved development efficiency and maintainability

Key Takeaway: Monorepo is not about being “advanced” but about achieving coordination.

Careful evaluation of project scale, team structure, and maintenance costs is essential before adoption.

💡 Further Reading & Open Source Practice

For more enterprise-level full-stack, frontend, backend, and Monorepo practices, you can Star or Watch the repository:

👉 https://github.com/aoda-zhang/PawHaven

Top comments (0)