Architectural Requirements Demo 2 - 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)
Mapping Quality Requirements to Architectural Decisions
| QR | Quality Requirement (quantified in Non-Functional Requirement Section) | Architectural Decision | Why it Satisfies the Requirement |
|---|---|---|---|
| QR1 | Performance - respond within 2s for 95% of requests, 500+ concurrent users | Application and database co-located in the same region; authentication state carried in a signed token rather than resolved via a database lookup; database connections released as soon as each service method completes | Removes cross-region latency from the request path, removes a database round-trip from every authenticated call, and prevents connection-pool exhaustion under load |
| QR2 | Scalability - support a 200% workload increase with no major architectural change | Stateless backend (no server-side session state); scaling limits held in configuration rather than code; the recommendation engine deployed as an independently scalable service | Any instance can serve any request without coordination; load headroom is raised by changing a value, not the design; API traffic and recommendation compute scale independently of each other |
| QR3 | Security - encryption for sensitive data, controlled access, no unauthorized modification | Signed, expiring tokens validated on every request with endpoints closed by default; ownership checks enforced in the service layer, not the controller; the recommendation engine holds no direct database credentials | Endpoints cannot be reached without a valid signature; no new endpoint can accidentally skip an ownership check, since the check lives below the routing layer; a compromised engine instance has nothing to exfiltrate |
| QR4 | Reliability & Availability - uptime target, recovery within 5 minutes of failure | Every deployed revision is retained so recovery is a traffic switch, not a rebuild; schema migrations run and are validated before a new version can receive traffic; health checks gate service startup against unready dependencies | Recovery time no longer depends on build time; a failed migration halts the release before any code goes live; a service never starts serving traffic against a dependency that isn't ready |
| QR5 | Maintainability - new features deployable within 2 hours, 80%+ automated coverage | Strict layer boundaries (Controller - Service - Repository - Entity); dependencies injected rather than hard-coded; a single automated deployment pipeline with no manual step between merge and release | Business logic is isolated and testable without a live server or database; test coverage is enforceable because logic doesn't depend on infrastructure; deployable within 2 hours is a pipeline property. |
updated 30 July 2026