Backend Architecture
Purpose
This document defines the backend architecture patterns, service communication, and domain boundaries for the Farmer1st platform.
Current State
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ BACKEND ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ CLIENTS │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Farmer PWA │ │ Stakeholder │ │
│ │ (React) │ │ Portals (React) │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ └──────────┬───────────┘ │
│ │ GraphQL │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ BFF LAYER (GraphQL) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ user-bff │ │relationship-│ │ surveys-bff │ │payments-bff │ │ │
│ │ │ │ │ bff │ │ │ │ │ │ │
│ │ │ GraphQL │ │ GraphQL │ │ GraphQL │ │ GraphQL │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │ │ │
│ └─────────┼───────────────┼───────────────┼───────────────┼──────────┘ │
│ │ REST │ REST │ REST │ REST │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MICROSERVICES LAYER (REST) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ user-management namespace │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │
│ │ │ │ auth-svc │ │profile-svc│ │ prefs-svc │ ... │ │ │
│ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ access-management namespace │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │
│ │ │ │invite-svc │ │ fga-svc │ │entity-svc │ ... │ │ │
│ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ surveys namespace │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │
│ │ │ │survey-svc │ │response-sv│ │analytics │ ... │ │ │
│ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ payments namespace │ │ │
│ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │
│ │ │ │points-svc │ │payout-svc │ │provider-sv│ ... │ │ │
│ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Decisions and Rationale
BFF Pattern (Backend for Frontend)
| Decision | Rationale |
|---|---|
| One BFF per domain | Clear ownership, independent scaling, domain-specific GraphQL schemas |
| GraphQL at BFF layer | Flexible queries for clients, reduces over-fetching, strong typing |
| BFF aggregates microservices | Single entry point per domain, hides internal service complexity |
Communication Patterns
| Layer | Protocol | Rationale |
|---|---|---|
| Client → BFF | GraphQL | Flexible queries, client-driven data needs, strong typing |
| BFF → Microservices | REST | Simple, stateless, well-understood, easy to test |
| Service → Service | REST + Kafka | REST for sync, Kafka for async/events |
Request Flow
┌─────────────────────────────────────────────────────────────────────────────┐
│ REQUEST FLOW EXAMPLE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Farmer opens app, views their profile and surveys │
│ │ │
│ ▼ │
│ 2. PWA sends GraphQL query: │
│ query { │
│ me { name, farm { name, location } } → user-bff │
│ mySurveys { id, title, status } → surveys-bff │
│ } │
│ │ │
│ ▼ │
│ 3. user-bff receives "me" query │
│ │ │
│ ├── GET /users/{id} → profile-service │
│ └── GET /farms?owner={id} → farm-service (or entity-service) │
│ │ │
│ ▼ │
│ 4. surveys-bff receives "mySurveys" query │
│ │ │
│ └── GET /surveys?user={id} → survey-service │
│ │ │
│ ▼ │
│ 5. BFFs aggregate responses, return unified GraphQL response │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Domain Boundaries
Known Domains
| Domain | Namespace | BFF | Responsibilities |
|---|---|---|---|
| User Management | user-management |
user-bff |
User profiles, authentication integration, preferences, farmer/stakeholder data |
| Access Management | access-management |
relationship-bff |
Invitations, OpenFGA tuple management, entity relationships, data access authorization |
| Surveys | surveys |
surveys-bff |
Survey creation, distribution, responses, analytics |
| Payments | payments |
payments-bff |
Points system, payouts, payment provider integrations |
Future Domains (TBD)
| Potential Domain | Possible Responsibilities |
|---|---|
| Farms | Farm management, plots, crops, certifications |
| Traceability | Supply chain tracking, product journey |
| Notifications | Push notifications, SMS, email |
| Analytics | Reporting, dashboards, insights |
BFF Design Principles
1. Domain Ownership
Each BFF owns its GraphQL schema and is the single entry point for that domain.
user-bff → owns: User, Profile, Preferences types
relationship-bff → owns: Invitation, Relationship, Access types
surveys-bff → owns: Survey, Question, Response types
payments-bff → owns: Points, Payout, Transaction types
2. No Cross-BFF Calls
BFFs should not call each other. If data from multiple domains is needed: - Client makes parallel GraphQL queries to multiple BFFs - Or use Kafka events for eventual consistency
3. BFF Responsibilities
- GraphQL schema definition and resolvers
- Request validation
- Aggregating data from multiple microservices
- Response transformation for client needs
- Caching (optional, at BFF level)
4. BFF Should NOT
- Contain business logic (belongs in microservices)
- Directly access databases (go through services)
- Make synchronous calls to other BFFs
Microservice Design Principles
1. Single Responsibility
Each microservice handles one specific capability within its domain.
2. Own Your Data
Each microservice owns its data and exposes it only via APIs.
3. Stateless
Services are stateless; state lives in databases, caches, or message queues.
4. REST API Standards
- RESTful resource naming
- Standard HTTP methods (GET, POST, PUT, DELETE)
- Consistent error responses
- OpenAPI/Swagger documentation
Service Communication
Synchronous (REST)
Used for: Real-time queries, CRUD operations, immediate responses needed.
BFF → Service: GET /users/{id}
Service → Service: POST /internal/validate-access
Asynchronous (Kafka)
Used for: Events, notifications, cross-domain updates, audit logging.
user-service → Kafka: UserCreated event
↓
access-management listens → Creates default OpenFGA tuples
surveys listens → Initializes user survey preferences
payments listens → Creates points account
Event-Driven Patterns
| Event | Producer | Consumers |
|---|---|---|
UserCreated |
user-management | access-management, surveys, payments |
InvitationAccepted |
access-management | user-management, notifications |
SurveyCompleted |
surveys | payments (award points), analytics |
PointsRedeemed |
payments | notifications, analytics |
Authorization Integration
All services integrate with OpenFGA for authorization checks:
┌─────────────────────────────────────────────────────────────────────────────┐
│ AUTHORIZATION FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Request arrives at BFF with JWT │
│ │ │
│ 2. BFF extracts user ID from JWT │
│ │ │
│ 3. BFF (or service) calls OpenFGA: │
│ Check(user:X, view, farm:Y) │
│ │ │
│ ├── Allowed → Continue with request │
│ └── Denied → Return 403 Forbidden │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Where to check authorization: - BFF level: For coarse-grained access (can user access this domain?) - Service level: For fine-grained access (can user access this specific resource?)
Technology Stack (Backend)
| Component | Technology | Notes |
|---|---|---|
| Language | Python | All backend services |
| Web Framework | TBD | FastAPI recommended for REST services |
| GraphQL Framework | TBD | Strawberry, Ariadne, or Graphene |
| API Documentation | OpenAPI/Swagger | For REST microservices |
Trade-offs Considered
GraphQL at BFF vs Gateway
| BFF GraphQL (Chosen) | Gateway GraphQL |
|---|---|
| ✅ Domain-specific schemas | ⚠️ Single massive schema |
| ✅ Independent deployments | ❌ Gateway becomes bottleneck |
| ✅ Clear ownership | ⚠️ Schema conflicts |
| ⚠️ Multiple endpoints for clients | ✅ Single endpoint |
Decision: BFF-level GraphQL for clear domain boundaries and independent scaling.
REST vs GraphQL for Service-to-Service
| REST (Chosen) | GraphQL |
|---|---|
| ✅ Simple, well-understood | ⚠️ Overhead for internal calls |
| ✅ Easy to test and debug | ⚠️ Schema complexity |
| ✅ Caching friendly | ✅ Flexible queries |
| ✅ OpenAPI tooling | ⚠️ Less tooling for internal |
Decision: REST for internal service communication; GraphQL complexity not needed internally.
Open Questions
- Python GraphQL framework: Strawberry vs Ariadne vs Graphene?
- Python REST framework: FastAPI vs Django REST vs Flask?
- API versioning strategy?
- Service mesh needed? (Istio, Linkerd)
- Rate limiting strategy per BFF?
- GraphQL federation needed in future?
Dependencies
- Kubernetes namespaces (see
03-infrastructure-architecture.md) - Repository structure (see
11-repository-structure.md) - OpenFGA for authorization (see
08-authorization-architecture.md) - Kafka for async events (see
05-data-architecture.md)
Last Updated: 2025-12-25