Skip to content

Repository Structure Decision: BFF Implementation

Date: December 2025
Status: Architecture Decision Record
Related: BFF-Implementation-Plan.md


Question

Should bast-bff be implemented in the same repository as bast-admin-ui (monorepo) or in a separate repository?


Context

  • Current State: bast-admin-ui is a React/TypeScript frontend in its own repository
  • New Component: bast-bff is a Node.js/TypeScript BFF layer using tRPC
  • Key Benefit: tRPC provides end-to-end type safety between frontend and BFF
  • Team: Frontend team will own the BFF layer

Options

Option A: Monorepo (Same Repository)

Structure:

bast-admin-ui/
├── apps/
│   ├── admin-ui/          # React frontend (current codebase)
│   │   ├── src/
│   │   ├── package.json
│   │   └── vite.config.ts
│   │
│   └── bff/               # Node.js BFF
│       ├── src/
│       ├── package.json
│       └── tsconfig.json
│
├── package.json           # Root workspace config
├── pnpm-workspace.yaml    # or npm/yarn workspaces
└── turbo.json             # Turborepo (optional)

Option B: Separate Repositories

Structure:

bast-admin-ui/             # Frontend repo
├── src/
├── package.json
└── ...

bast-bff/                  # BFF repo (separate)
├── src/
├── package.json
└── ...


Comparison Matrix

Criteria Monorepo (Option A) Separate Repos (Option B)
Type Sharing ✅ Native - shared types via tRPC ⚠️ Requires package publishing or git submodules
Development Workflow ✅ Single clone, single install ⚠️ Multiple repos to manage
CI/CD Complexity ⚠️ More complex (need to detect changes) ✅ Simpler (independent pipelines)
Deployment Independence ⚠️ Coupled (but can be decoupled) ✅ Fully independent
Code Changes ✅ Atomic commits across frontend + BFF ⚠️ Requires coordination across repos
Team Collaboration ✅ Easier for frontend team ⚠️ More coordination needed
Build Times ⚠️ Slower (builds both) ✅ Faster (builds independently)
Dependency Management ✅ Shared dependencies ⚠️ Duplicate dependencies
Testing ✅ Can test integration easily ⚠️ Harder to test integration
tRPC Benefits ✅ Maximum benefit (shared types) ⚠️ Reduced benefit (types across repos)

Detailed Analysis

Option A: Monorepo - Pros

  1. Perfect tRPC Integration
  2. Types are shared automatically
  3. No code generation needed
  4. TypeScript can reference types directly
  5. Changes to BFF types immediately reflected in frontend

  6. Atomic Changes

  7. Can update frontend and BFF in a single commit
  8. Easier to maintain API contracts
  9. Better for refactoring

  10. Simplified Development

  11. Single npm install or pnpm install
  12. One repository to clone
  13. Easier onboarding for new developers

  14. Better DX (Developer Experience)

  15. IDE can navigate between frontend and BFF code
  16. Easier to understand the full stack
  17. Better autocomplete and type checking

  18. Shared Code

  19. Can share utilities, types, constants
  20. Single source of truth for shared logic

Option A: Monorepo - Cons

  1. CI/CD Complexity
  2. Need to detect which packages changed
  3. May build/test both when only one changed
  4. Requires tools like Turborepo, Nx, or custom scripts

  5. Repository Size

  6. Larger repository
  7. Longer clone times

  8. Deployment Coupling

  9. Frontend and BFF deployments are in same repo
  10. Need careful branch management
  11. Can be mitigated with proper CI/CD setup

  12. Access Control

  13. Harder to restrict access to specific parts
  14. Everyone with repo access sees everything

Option B: Separate Repos - Pros

  1. Independent Deployment
  2. Deploy frontend and BFF independently
  3. Different release cycles
  4. Easier to scale teams

  5. Simpler CI/CD

  6. Each repo has its own pipeline
  7. No need to detect changes
  8. Faster builds (only builds what changed)

  9. Clear Boundaries

  10. Clear separation of concerns
  11. Easier to understand ownership
  12. Better for large teams

  13. Access Control

  14. Can restrict access per repository
  15. Different teams can own different repos

Option B: Separate Repos - Cons

  1. Type Sharing Complexity
  2. Need to publish BFF types as npm package
  3. Or use git submodules (complex)
  4. Or generate types (defeats tRPC purpose)

  5. Development Friction

  6. Need to clone multiple repos
  7. Need to manage multiple dependencies
  8. Harder to make coordinated changes

  9. Reduced tRPC Benefits

  10. Types may not be perfectly synced
  11. Need to regenerate types when BFF changes
  12. Less seamless developer experience

  13. Coordination Overhead

  14. Need to coordinate changes across repos
  15. API changes require PRs in multiple repos
  16. Harder to maintain consistency

Recommendation: Option A (Monorepo)

Rationale

Given your specific context:

  1. tRPC is the Key Benefit
  2. The primary reason for choosing BFF pattern is end-to-end type safety
  3. Monorepo maximizes this benefit
  4. Separate repos would require workarounds that reduce tRPC's value

  5. Frontend Team Ownership

  6. Since frontend team owns the BFF, monorepo makes sense
  7. Easier collaboration within the team
  8. No cross-repo coordination needed

  9. Small to Medium Team

  10. Monorepo works well for smaller teams
  11. Less overhead than managing multiple repos

  12. Rapid Development

  13. Can iterate quickly on both frontend and BFF
  14. Changes are atomic and easier to test

Implementation Approach

Recommended Structure:

bast-admin-ui/                    # Monorepo root
├── apps/
│   ├── admin-ui/                 # React frontend
│   │   ├── src/
│   │   ├── package.json
│   │   └── vite.config.ts
│   │
│   └── bff/                      # Node.js BFF
│       ├── src/
│       ├── package.json
│       └── tsconfig.json
│
├── packages/
│   └── shared/                   # Shared types & utilities (optional)
│       ├── types/
│       └── package.json
│
├── package.json                  # Root workspace
├── pnpm-workspace.yaml           # Workspace config
├── turbo.json                    # Turborepo config (optional)
└── .github/
    └── workflows/
        └── ci.yml                # CI/CD with change detection

Tools:

  1. Package Manager: Use pnpm workspaces or npm workspaces
  2. Build Tool (Optional): Turborepo or Nx for faster builds
  3. Type Sharing: Direct TypeScript imports (no package publishing needed)

Alternative: Hybrid Approach

If you need deployment independence but want type sharing:

Structure: - Keep repos separate - Use tRPC with code generation - Publish BFF types as npm package - Frontend imports types from package

Trade-off: More setup complexity, but maintains deployment independence.


Migration Path

If Choosing Monorepo:

  1. Phase 1: Restructure Current Repo

    # Move current code to apps/admin-ui/
    mkdir -p apps/admin-ui
    git mv src apps/admin-ui/
    git mv package.json apps/admin-ui/
    git mv vite.config.ts apps/admin-ui/
    

  2. Phase 2: Add BFF

    # Create BFF in apps/bff/
    mkdir -p apps/bff
    cd apps/bff
    npm init -y
    # Follow BFF implementation plan
    

  3. Phase 3: Set Up Workspace

    # Root package.json
    {
      "name": "bast-admin-monorepo",
      "private": true,
      "workspaces": [
        "apps/*",
        "packages/*"
      ],
      "scripts": {
        "dev": "pnpm --filter admin-ui dev & pnpm --filter bff dev",
        "build": "pnpm --filter '*' build"
      }
    }
    

  4. Phase 4: Configure tRPC Type Sharing

    // apps/admin-ui/src/lib/trpc.ts
    import type { AppRouter } from '../../bff/src/trpc/router';
    
    // Direct import - types are shared!
    


Decision Checklist

Choose Monorepo if: - ✅ You want maximum tRPC type safety benefits - ✅ Frontend team owns BFF - ✅ Team size is small to medium (< 10 developers) - ✅ You want rapid iteration - ✅ You're okay with slightly more complex CI/CD

Choose Separate Repos if: - ✅ You need strict deployment independence - ✅ Different teams own frontend vs BFF - ✅ You have complex access control requirements - ✅ You prefer simpler CI/CD pipelines - ✅ You're willing to trade some tRPC benefits for independence


Final Recommendation

For Bast Admin UI: Use Monorepo (Option A)

The benefits of seamless type sharing with tRPC outweigh the CI/CD complexity. Since the frontend team owns the BFF, a monorepo provides the best developer experience and maintains the core value proposition of the BFF pattern.


Next Steps

  1. ✅ Decision Made: Proceed with monorepo structure
  2. Update BFF Implementation Plan to reflect monorepo structure
  3. Set up workspace configuration (pnpm/npm workspaces)
  4. Restructure repository (move frontend to apps/admin-ui/)
  5. Create BFF in apps/bff/
  6. Configure CI/CD with change detection

Document created for Bast Admin UI Repository Structure Decision