Skip to main content

Architecture Overview

TamborraData follows a clean, layered architecture with strict separation of concerns. The application is organized into distinct layers, each with a single responsibility, making it maintainable, testable, and scalable.

Architecture Diagram

Layered Architecture

The application is organized into five distinct layers:
Location: app/(frontend)/Responsibilities:
  • Rendering UI components
  • Client-side interactivity
  • Data fetching with React Query
  • Server Components for SEO and performance
Technologies:
  • Next.js 16 App Router
  • React Server Components
  • TanStack React Query
  • TailwindCSS
Example structure:
app/(frontend)/
├── statistics/
│   ├── page.tsx              # Server Component
│   ├── components/           # Feature components
│   ├── hooks/                # React Query hooks
│   └── services/             # HTTP calls
Location: app/(backend)/api/Responsibilities:
  • HTTP request handling
  • Request/response validation
  • Error handling
  • Query parameter parsing
Pattern: Each API route follows this structure:
api/[feature]/
├── route.ts              # HTTP handler
├── dtos/                 # Validation schemas
├── services/             # Business logic
├── repositories/         # Data access
└── types/                # TypeScript types
Example: app/(backend)/api/statistics/route.ts:7
Location: app/(backend)/api/*/services/Responsibilities:
  • Business logic implementation
  • Data transformation and formatting
  • Orchestrating repository calls
  • Error handling and logging
Example: app/(backend)/api/statistics/services/statistics.service.ts:7Services contain NO database queries, only business logic.
Location: app/(backend)/api/*/repositories/Responsibilities:
  • Database queries
  • Data access abstraction
  • SQL/ORM operations
  • Connection management
Example: app/(backend)/api/statistics/repositories/statistics.repo.ts:6Repositories contain ONLY data access code, no business logic.
Location: Supabase PostgreSQLResponsibilities:
  • Data persistence
  • Row Level Security (RLS)
  • Constraints and indexes
  • Triggers and functions
Configuration: app/(backend)/core/db/supabaseClient.ts:6

Request Flow Example

Here’s how a typical request flows through the layers:
1

User Interaction

User visits /statistics/2024 page
2

Presentation Layer

Server Component renders, React Query hook calls service
// app/(frontend)/hooks/query/useStatisticsQuery.ts
const { data } = useStatisticsQuery('2024');
3

HTTP Request

Service makes HTTP request to API route
// app/(frontend)/services/fetchStatistics.ts
fetch('/api/statistics?year=2024')
4

API Layer

Route handler validates parameters and calls service
// app/(backend)/api/statistics/route.ts
const { valid, cleanYear } = await checkParams(year);
const { statistics } = await getStatistics(cleanYear);
5

Business Layer

Service processes data and calls repository
// services/statistics.service.ts
const { statistics } = await fetchStatistics(year);
const grouped = groupBy(statistics, 'category');
6

Data Layer

Repository executes database query
// repositories/statistics.repo.ts
await supabaseClient
  .from('statistics')
  .select('*')
  .eq('year', year);
7

Database Layer

PostgreSQL executes query with RLS policies applied
8

Response

Data flows back up through all layers to the UI

Design Principles

Separation of Concerns

Each layer has a single, well-defined responsibility. UI code doesn’t touch databases, repositories don’t handle HTTP.

Dependency Direction

Dependencies flow downward only. Upper layers depend on lower layers, never the reverse.

Abstraction

Each layer abstracts implementation details. Services don’t know if data comes from PostgreSQL, MongoDB, or REST API.

Testability

Each layer can be tested independently by mocking the layer below it.

Benefits of This Architecture

  • Clear organization makes code easy to find
  • Changes in one layer don’t affect others
  • New features follow established patterns
  • Code reviews are more focused
  • Mock repositories for service tests
  • Mock services for API route tests
  • Integration tests at layer boundaries
  • Unit tests within each layer
  • Easy to add new features
  • Repositories can be optimized independently
  • Services can be extracted to microservices if needed
  • Caching can be added at any layer
  • Database security enforced by RLS
  • Input validation at API layer
  • Business rules in service layer
  • No SQL injection risk with proper abstraction

Project Structure

The codebase is organized by feature, not by file type:
app/
├── (frontend)/              # Presentation Layer
   ├── statistics/
   ├── page.tsx
   ├── components/
   ├── hooks/
   └── services/
   └── search/
       ├── page.tsx
       ├── components/
       └── hooks/
└── (backend)/               # API + Business + Data Layers
    ├── api/
   ├── statistics/
   ├── route.ts     # API Layer
   ├── services/    # Business Layer
   ├── repositories/# Data Layer
   ├── dtos/
   └── types/
   └── participants/
       ├── route.ts
       ├── services/
       └── repositories/
    ├── core/
   └── db/              # Database client
    └── shared/
        └── utils/           # Shared utilities
Feature-based organization makes it easy to:
  • Find all code related to a feature
  • Delete or refactor features without affecting others
  • Understand the scope of changes
  • Reduce merge conflicts

Code Organization Rules

Important conventions:
  1. Server-only code must have import 'server-only' at the top
  2. Client Components must have 'use client' directive
  3. Repositories never contain business logic
  4. Services never contain SQL queries
  5. Routes only handle HTTP concerns

Comparison with Alternatives

ApproachTamborraDataMonolithicMicroservices
Complexity✅ Moderate✅ Low❌ High
Maintainability✅ High❌ Low⚠️ Medium
Performance✅ Fast✅ Fast⚠️ Network overhead
Testability✅ Easy❌ Hard✅ Easy
Scalability✅ Good❌ Limited✅ Excellent
Team Size✅ Small-Medium✅ Small❌ Large
Why layered monolith? TamborraData uses a modular monolith approach that provides most benefits of microservices without the operational complexity. This is ideal for small-to-medium applications with a small team.

Next Steps

Explore each layer in detail:

Frontend

Server Components, React Query, and UI patterns

Backend

API Routes, Services, and Repository pattern

Database

PostgreSQL schema, RLS policies, and Supabase

References

Build docs developers (and LLMs) love