Introduction
Accountability is a multi-company, multi-currency accounting SaaS application built with a strict separation between backend and frontend. The backend uses Effect-TS for functional, type-safe business logic, while the frontend uses React with TanStack Start for server-side rendering.Critical Design Principle: Backend and frontend must stay aligned. Frontend-only workarounds are not acceptable. All features must be implemented across all layers: Frontend → API → Service → Repository → Database.
System Architecture
Architectural Layers
1. Frontend Layer (packages/web)
Technology: React, TanStack Start, openapi-fetch, Tailwind CSS Responsibilities:- Server-side rendering (SSR) with TanStack Start loaders
- Client-side interactivity with React hooks
- Type-safe API calls using generated openapi-fetch client
- UI state management with
useStateanduseReducer - No Effect code - pure React patterns
- Data fetching in route
loader()functions for SSR - Client-side mutations with
router.invalidate()for refetching - Tailwind CSS for styling (no inline styles)
- Component composition over prop drilling
2. API Layer (packages/api)
Technology: Effect HttpApi, HttpApiBuilder Responsibilities:- HTTP endpoint definitions with typed schemas
- Request/response validation using Effect Schema
- Authentication and authorization middleware
- OpenAPI specification generation
- Error mapping from domain to HTTP status codes
HttpApiEndpointfor endpoint definitionsHttpApiGroupfor organizing related endpointsHttpApiMiddlewarefor cross-cutting concerns- Cookie-based authentication with httpOnly cookies
3. Core Layer (packages/core)
Technology: Effect, Effect Schema Responsibilities:- Domain entities (Account, Company, JournalEntry, etc.)
- Business logic services (AccountService, ReportingService, etc.)
- Value objects (CurrencyCode, AccountId, MonetaryAmount, etc.)
- Domain errors as Schema.TaggedError
- 100% test coverage requirement
Schema.Classfor domain entitiesContext.Tagfor service definitionsLayer.effectandLayer.scopedfor service implementations- Branded types for IDs (AccountId, CompanyId, etc.)
- Effect generators for composing operations
4. Persistence Layer (packages/persistence)
Technology: @effect/sql, @effect/sql-pg, PostgreSQL Responsibilities:- Repository interfaces and implementations
- Database schema migrations
- SQL query construction with type safety
- Transaction management
- Database connection pooling
SqlSchema.findOne,SqlSchema.findAllfor queriesSqlSchema.voidfor commands (INSERT/UPDATE/DELETE)Model.Classfor repository entities- Transaction support via
sql.withTransaction
Data Flow
Request Flow (Read)
Mutation Flow (Write)
Key Design Decisions
Effect on Backend Only
Decision: Use Effect-TS exclusively for backend code (core, persistence, api packages). Frontend uses standard React patterns. Rationale:- Effect’s learning curve is steep - limiting to backend reduces complexity
- Backend benefits most from Effect’s type safety and error handling
- Frontend developers can work with familiar React patterns
- Clear separation of concerns between layers
Type-Safe API Contract
Decision: Generate OpenAPI spec from Effect HttpApi and use it to generate typed fetch client for frontend. Rationale:- Single source of truth for API contract (Effect schemas)
- Compile-time type safety across frontend/backend boundary
- Automatic client code generation reduces boilerplate
- Schema changes automatically propagate to frontend types
No Barrel Files
Decision: Import from specific modules, never create index.ts barrel files. Rationale:- Avoids circular dependency issues
- Explicit imports make dependencies clear
- Better tree-shaking for production bundles
- Easier to trace where code is defined
Flat Module Structure
Decision: Organize code by domain with flat file structure (e.g.,accounting/Account.ts, not accounting/entities/Account/index.ts).
Rationale:
- Simpler navigation and fewer nesting levels
- Clear naming conventions prevent collisions
- Easier to find code with text search
- Reduced cognitive load
Multi-Tenancy Model
Organization-Level Isolation
Accountability uses organization-level multi-tenancy:- Each organization owns multiple companies
- Users belong to organizations via memberships
- All queries are scoped by organizationId
- Single shared PostgreSQL database with row-level security
Authorization
RBAC (Role-Based Access Control) with functional roles:- Admin: Full access to organization
- AccountingManager: Manage chart of accounts, journal entries
- FinancialAnalyst: Read-only access to reports
- Auditor: Read-only access to all financial data
- Organization membership required
- Company-specific permissions
- Period lock enforcement (no entries in closed periods)
Scalability Considerations
Current Architecture
- Monolithic deployment: All packages deployed together
- Single PostgreSQL database: Shared across all organizations
- SSR + Client-side hydration: Fast initial page loads
- Connection pooling: Efficient database resource usage
Future Scaling Options
- Horizontal scaling: Deploy multiple instances behind load balancer
- Read replicas: Separate read/write database connections
- Caching layer: Redis for frequently accessed data (exchange rates, chart of accounts)
- CDN: Static assets and client-side bundles
- Database partitioning: Partition tables by organizationId
Security Architecture
Authentication
- Multi-provider support (Local, Google OAuth, WorkOS SSO)
- httpOnly cookies for session tokens (XSS protection)
- Secure, sameSite=strict cookies (CSRF protection)
- Session expiration and rotation
Authorization
- Policy-based authorization engine
- Middleware enforcement at API layer
- Service-layer validation for defense in depth
- Audit logging for all authorization decisions
Data Protection
- TLS/HTTPS for all connections
- Encrypted database connections
- No sensitive data in logs
- Regular security audits
Error Handling
Three-Layer Error Architecture
- Domain Errors (core): Business logic errors (AccountNotFound, ValidationError)
- API Errors (api): HTTP-appropriate errors with status codes
- Frontend Errors (web): User-friendly error messages with recovery actions
Testing Strategy
Test Pyramid
- Core package: 100% test coverage
- Persistence: 90% test coverage
- API: 80% test coverage
- Frontend: E2E coverage of critical flows
Development Workflow
Local Development
Pre-Commit Checklist
- All tests pass (
pnpm test) - Type checking passes (
pnpm typecheck) - Linting passes (
pnpm lint) - Code formatted (
pnpm format) - API client regenerated if API changed
- Migration created if schema changed
Next Steps
Explore the detailed architecture documentation:- Tech Stack - Technologies and dependencies
- Project Structure - Monorepo organization
- Effect Framework - Effect patterns and best practices
- Domain Model - Business entities and value objects
- Persistence - Database layer and SQL patterns
- Frontend Architecture - React and UI patterns
- SSR and Routing - TanStack Start patterns
- Testing - Testing strategy and patterns
- Error Handling - Error architecture and patterns