Architectural Requirements Demo 3 - COS301-SE-2026/Mealchemy GitHub Wiki
Architectural Requirements
Table of Contents
- Architectural Patterns
- Design Patterns
- Architectural Constraints
- Architectural Diagram
- Mapping Quality Requirements to Architectural Decisions
Architectural Patterns
Mealchemy follows a layered 4-tier architecture combined with a client-server communication model. The system is separated into presentation, access, services, persistence, and recommendation-processing responsibilities. This separation of concerns improves maintainability, modularity, scalability, and independent development across frontend, backend, database, and recommendation components.
Client-Server Architecture
Mealchemy follows a client-server architecture. The mobile frontend acts as the client and is responsible for presenting the user interface and capturing user interactions. The backend server is responsible for processing requests, enforcing business rules, managing authentication, coordinating recommendation processing, and accessing stored application data.
The frontend does not communicate directly with the database. The API sits between the client (presentation layer) and the services layer, providing a single point of entry for requests. All requests and data operations are routed through the backend to ensure security, validation, and consistent application behaviour.
Layered 4-Tier Architecture
The backend follows a layered structure where each layer has a specific responsibility:
- Controller Layer: Receives requests from the frontend and returns responses.
- Service Layer: Contains business logic, validation, and application rules.
- Repository Layer: Handles data access and persistence operations.
- Entity/Model Layer: Represents persistent application data.
Mealchemy also applies MVC style separation within this layered architecture. Controllers manage incoming HTTP requests, models/entities represent application data, and services coordinate business logic before data is accessed through repositories.
This structure improves modularity and maintainability by separating request handling, business logic, and database access responsibilities.
Recommendation Service Architecture
Mealchemy includes a modular recommendation service responsible for heuristic-based recommendation processing. The recommendation service supports:
- Preference filtering
- Pantry matching
- Recipe ranking
The recommendation service operates independently from the main backend persistence layer and does not directly access the database. The backend acts as a middle man between the client and the recommendation service, as the client never directly accesses the recommendation service. Instead, it communicates with the backend through API requests and the backend forwards the necessary context and preference data to the recommendation engine where the necessary logic is performed and the results are returned to the backend. The backend remains responsible for data persistence, validation, and ownership checks.
Separating recommendation processing from the core backend improves modularity and allows recommendation functionality to evolve independently while remaining integrated with the main application services.
Repository Pattern
Mealchemy uses the Repository Pattern to separate business logic from persistence operations. Services interact with repositories rather than directly querying the database.
Repositories are responsible for retrieving, storing, and updating persistent application data such as users, recipes, pantry ingredients, shopping lists, and preferences.
This pattern improves maintainability, testability, and separation of concerns by isolating database access logic from application services.
Client-Side State Management
The frontend follows a feauture first MVVM (Model, View, ViewModel) architecture, where each feature module contains its own Model, View (screens and widgets), ViewModel (providers), and repository with a shared service layer handleing all the outgoing HTTP calls to the backend.
The frontend follows a provider-based state management pattern using Riverpod. Application state is managed through providers rather than directly within UI components.
This approach keeps the frontend modular and improves separation between presentation logic, business logic, and data retrieval. It also supports dependency injection and allows frontend components to switch between mock repositories and backend repositories during development and testing.
Design Patterns
Mealchemy uses selected object-oriented design patterns to improve modularity, maintainability, extensibility, and separation of concerns across the frontend, backend, and recommendation systems.
The proposed participants and implementations described below are based on the current system design and domain model and may evolve as implementation progresses.
Strategy Pattern
Purpose
The Strategy Pattern is used when multiple interchangable algorithms or processing approaches are required for the same operation.
Mealchemy Participants
- Context: RecommendationEngine
- Strategy Interface: RecommendationStrategy
- Concrete Strategies:
- PantryCoverageBasedRecommendationStrategy
- CuisineAffinityBasedRecommendationStrategy
- NutritionalGoalBasedRecommendationStrategy
- FreshnessBasedRecommendationStrategy
- NoveltyRecommendationStrategy
- Supporting Domain Classes:
- RecipeIngredient
- PantryIngredients
- IngredientCatalogue
- UserPreferences
- CuisineAffinities
- PreferenceWeights
- DiscoverySwipe
Use in Mealchemy
Mealchemy generates recipe recommendations using several forms of user context, including dietary restrictions, pantry contents and ingredient freshness, flavour preferences (cuisine affinities), nutritional goals, and discovery history.
The domain model includes classes such as PantryIngredients, RecipeIngredient, IngredientCatalogue, UserPreferences, CuisineAffinities, PreferenceWeights, DiscoverySwipe, all of which contribute to recommendation generation.
Using the Strategy Pattern allows the recommendation engine to switch between or combine different recommendation algorithms without changing the rest of the system.
Prototype Pattern
Purpose
Specifies the kind of object to create using a prototypical instance, and creates new objects by copying this prototype
Mealchemy Participants
- Prototype: Recipe - implements the cloning directly. No separate cloning interface exists as Recipe is the only cloneable type in the domain model (Prototype and ConcretePrototype are collapsed into a single class here)
- Client: RecipeService (Invokes the cloning of a recipe)
Use in Mealchemy
The domain model shows Recipes which can be copied from a shared vault or the global vault (community repository) and saved to the user's private vault.
Instead of manually reconstructing a copy field by field, the Prototype Pattern allows the existing recipe, including its ingredients and steps, to be cloned directly. The sourceId of the copy is set to reference the recipeId of the original recipe.
Facade Pattern
Purpose
The Facade Pattern provides a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
Mealchemy Participants
-
Facade: Access Layer API (Controllers)
-
Subsystem classes:
- AuthService
- PreferenceService
- IngredientService
- VaultService
- RecipeService
- PantryService
- ShoppingListService
- RecommendationEngine (Invoked through RecommendationService)
Use in Mealchemy
The Facade (access layer) is the single entry point the frontend talks to, which hides the backend's service, repository and recommendation engine complexity from the frontend. The API knows which subsystem is responsible for the request and delegates the work to the the appropriate subsystem accordingly.
Using the Facade Pattern allows the frontend to be shielded from backend complexity, emphasising the separation of concerns. This improves maintainability, as frontend caller is not tightly coupled to the backend logic. The subsystems can change internally without affecting the client-facing contract.
Adapter Pattern
Purpose
The Adapter Pattern is used to integrate external systems while isolating the rest of the application from external API structures or protocol differences.
Mealchemy Participants
-
Target Interface: Internal nutrition service interface
-
Adapters: NutritionDataAdapter
-
Adaptees: External nutrition service (e.g. USDA)
Use in Mealchemy
Mealchemy integrates with external systems for nutritional information.
Each external service may expose different request formats, authentication methods, or response structures. Adapter classes convert external responses into a consistent internal format used by Mealchemy services.
This reduces coupling between the application and third-party systems and simplifies future API replacement or modification.
Command Pattern
Purpose
Encapsulate a request as an object, thereby letting you parameterise clients with different requests, queue or log requests, and support undoable operations.
Mealchemy Participants
- Command Interface: SwipeCommand
- Concrete Commands:
- LikeSwipeCommand
- PassSwipeCommand
- Invoker: DiscoverySessionManager (holds a stack of recently executed commands)
- Receiver: DiscoverySwipeService (performs the actual swipe recording/removal)
Use in Mealchemy
Each swipe action (like or pass) during the guided discovery session is wrapped in a Command object rather than being applied directly.
The DiscoverySessionManager pushes each executed command onto a stack as the user swipes. When the user taps undo, the manager pops the most recent command off the stack and calls its undo() method, reversing that action without needing to know if it was liked or passed.
This keeps the undo logic isolated from the swipe-handling logic itself.
Architectural Constraints
Cost
- All technologies, services, and hosting platforms must remain within free tier limits.
- No paid APIs or services may be introduced.
Platform Support
- The system must run on both iOS and Android.
Security
- No passwords, secrets, or API keys may be hardcoded in source files.
- All credentials are injected at runtime via environment variables.
Database
- PostgreSQL is accessed exclusively by the Spring Boot backend.
- No other service writes to the database directly.
- Flyway migration files are immutable once committed and run, all schema changes require a new versioned migration file
- [However during development with Docker, the database volume can we wiped upon editing existing migration files - this can no longer be done as the database has been deployed]
Backend
- The backend holds no server-side session state, all authentication state lives in the JWT token
- The recommendation engine communicates exclusively through the Spring Boot REST API and has no database connection of its own
Layer Boundaries
- Controllers may only call services, services may only call repositories or dedicated external service clients, repositories may only interact with the database, no layer may skip or bypass an adjacent layer.
API Design
- All backend endpoints must follow RESTful conventions, no business logic may be exposed through query parameters or non-standard HTTP methods.
Testing
- All three services must maintain a minimum of 80% code coverage, no code may be merged without passing the full CI pipeline.
Architectural Diagram
View Architectural Diagram (PDF)
flowchart TB
subgraph PRES["Presentation Layer"]
UP["User Profile"]
RVa["Recipe Vaults"]
RV["Recipe View"]
PA["Pantry"]
SL["Shopping List"]
GD["Guided Discovery"]
end
subgraph OFF["Offline Support - client-side"]
NS["Network Status<br>connectivity + reachability"]
LC[("Local Cache<br>Drift / SQLite<br>user-scoped ยท read-only")]
end
subgraph ACC["Access Layer"]
API["API"]
end
subgraph SVC["Services Layer"]
AUTH["Authentication"]
PREF["Preferences"]
VLT["Vault"]
RM["Recipe Management"]
SLS["Shopping List"]
PAN["Pantry"]
ING["Ingredient"]
end
subgraph REC["Recommendation Service"]
PF["Preference Filter"]
HE["Heuristic Engine"]
RP["Recommendation Processor"]
end
subgraph EXT["External Systems Layer"]
INDS["Ingredient and<br>Nutrition Data Service"]
end
subgraph PERS["Persistence Layer"]
DB[("User<br>Recipe Vault<br>Pantry<br>Shopping List<br>Ingredient Catalogue<br>Ingredient Category")]
end
subgraph s1["Legend"]
n1["-----------> : dotted - offline / non-runtime relationship<br>โโโโโโโโ> : solid - primary request path"]
end
PRES L_PRES_ACC_0@<-- request / response --> ACC
SVC <--> PERS
SVC <-- invoked via backend only --> REC
EXT -- ingredient / nutrition data --> SVC
LC L_LC_PERS_0@-. caches subset of .-> PERS
NS -. online / offline signal .-> PRES
ACC <--> SVC
LC L_LC_PRES_0@<-. "cache-aside read" .-> PRES
style PRES fill:#b0e3e6,stroke:#0e8088,color:#000
style ACC fill:#ffe6cc,stroke:#d79b00,color:#000
style SVC fill:#fff2cc,stroke:#d6b656,color:#000
style PERS fill:#e1d5e7,stroke:#9673a6,color:#000
style REC fill:#dae8fc,stroke:#6c8ebf,color:#000
style EXT fill:#d5e8d4,stroke:#82b366,color:#000
style OFF fill:#f8cecc,stroke:#b85450,color:#000
L_PRES_ACC_0@{ curve: linear }
L_LC_PERS_0@{ curve: linear }
L_LC_PRES_0@{ curve: linear }
Note: the diagram above shows the high-level architecture only. See the Detailed Design diagram (Frontend Component Architecture and Backend Layer Architecture) at View Detailed Architectural Diagram (PDF), which is not represented in the mermaid diagram above.
Mapping Quality Requirements to Architectural Decisions
| QR | Quality Requirement | Quantified Requirement | Architectural Decision | Why it Satisfies the Requirement |
|---|---|---|---|---|
| QR1 | Performance | At a 10-user baseline and 20-user peak for 5 minutes each, p95 latency <= 2 seconds, error rate < 1%, and peak throughput >= 1.8x baseline | Cloud Run and Neon deployed in regional proximity; bounded database connection pool; connections released when service methods complete; stateless request handling | Regional proximity reduces database network delay, early connection release limits pool contention, and stateless request handling allows concurrent reads to be distributed across instances |
| QR2 | Scalability | At 100 concurrent users for 5 minutes, >= 99% successful responses, no service crash, and no manual recovery; latency recorded as an observation | Stateless backend; Cloud Run automatic scaling and managed restart; deployment capped at three instances for cost control | Any instance can serve any request, Cloud Run can add capacity and replace unhealthy instances automatically, and the explicit instance cap explains why latency degrades at high load |
| QR3 | Security | 100% of authorization and ownership checks pass; 0 verified secrets; 0 high or critical container findings; 0 unresolved medium-or-higher API findings | JWT validation with deny-by-default rules; ownership checks in the service layer; Secret Manager; Gitleaks; Trivy; input validation, security headers, and limited API exposure | Protected routes reject missing or invalid credentials, ownership rules apply below the controller, secrets stay outside source code, and automated scans block serious repository, image, and API findings |
| QR4 | Reliability and Availability | >= 99.9% availability over 30 days; 20 of 20 controlled engine-failure responses within 3 seconds with 0 unhandled HTTP 500 responses; 4 of 4 offline collections readable; 0 offline writes and cross-user reads | Health checks, managed restart, and external monitoring; backend timeout and defined engine fallback; viewer-scoped SQLite cache and repository fallback; offline mutation interceptor and composite cache keys | Monitoring measures service availability, timeout and fallback contain engine failures, cached repositories preserve supported reads, and viewer-scoped keys with blocked mutations protect offline data |
| QR5 | Maintainability | Backend and frontend coverage >= 80%; 0 accessibility-guideline failures on named priority screens | Controller-service-repository layering; dependency injection; JaCoCo and lcov coverage gates in CI; accessible Flutter components and guideline tests | Isolated dependencies keep application logic testable, CI prevents coverage from falling below the threshold, and guideline tests detect accessibility regressions in priority workflows |
Updated 3 September 2026