SHOPPYCAT HOUSEHOLD SYSTEM // S07
Platform Architecture
A Compose application, focused domain repositories, reactive ViewModels, Firebase services, local preferences, and portable parsing rules keep a broad product understandable.
Android and Firebase activeSHOPPYCAT.APP ↗Separate the reason from the mechanism
Domain models describe shopping lists, pantry items, recipes, meal plans, households, invitations, activity, allergens, suggestions, themes, and users without embedding interface code. Repository interfaces divide list, pantry, recipe, family, household, and activity responsibilities.
The Firebase repository composes those interfaces for the current app. ViewModels depend on the smallest useful boundary where practical, expose immutable StateFlow state, and launch asynchronous work inside their lifecycle. Jetpack Compose renders that state rather than owning the source of truth.
Current production stack
- Kotlin and Jetpack Compose with Material 3
- Hilt dependency injection
- Coroutines, Flow, StateFlow, and callbackFlow
- Firebase Authentication, Firestore, Storage, Functions, and Cloud Messaging
- DataStore-backed language, region, unit, naming, notification, and interface preferences
- CameraX, ML Kit barcode scanning, text recognition, and speech services
- OkHttp, Jsoup, and Kotlin serialization for recipe and product integration
- Google Play Billing with server-synchronized entitlement
Narrow repositories communicate intent
A screen that only needs recipes should not inherit every operation in the product. Small domain contracts make dependencies visible, permit focused tests, and keep platform mechanisms behind the boundary.
' Describe only the capabilities recipe workflows require.
INTERFACE RecipeStore
FUNCTION ObserveRecipes()
FUNCTION SaveRecipe(recipe)
FUNCTION DeleteRecipe(recipeId)
FUNCTION AddIngredientsToList(recipeId, listId, scale)
FUNCTION ApplyIngredientsToPantry(recipeId, scale, direction)
END INTERFACE
FUNCTION PrepareRecipeForFour(store, recipe)
' Scaling belongs to the workflow, not to a particular screen widget.
scale = 4 / MAX(1, recipe.Servings)
store.AddIngredientsToList(recipe.Id, "weekly-shop", scale)
END FUNCTIONThe web port is a product boundary
Current documentation maps Android features to a browser implementation and records which operations use Auth, Firestore, callable Functions, Storage, or external product data. The browser extension already explores recipe handoff into a web review surface.
The goal is not a visual clone of Android. Shared rules and data contracts should produce equivalent outcomes while each platform handles input, navigation, permissions, and rendering in the way its users expect.
A narrow interface is a design decision, not a limitation
RecipeStore, StateFlow, and Compose are ShoppyCat's specific stack choices. Another codebase will use different frameworks entirely. The reason the repository interfaces are shaped the way they are is what is worth taking into a project that shares none of these technologies.
The same argument justifies small, focused interfaces in any layered codebase: a repository pattern in a web backend, a hexagonal architecture's ports and adapters, a microservice's public API surface. Depend on the smallest contract that does the job, and the rest of the system stays free to change underneath it.
- RecipeStore exposes exactly five operations, observe, save, delete, add ingredients to a list, apply ingredients to pantry, instead of one large repository every screen depends on. A screen that only touches recipes should only be able to see recipe operations; narrow contracts make a dependency list double as documentation of what a piece of code can actually do.
- Domain models describe shopping lists, pantry items, and recipes without embedding any interface code, and ViewModels expose immutable StateFlow rather than mutable state a view could corrupt. Keeping the shape of the data separate from how it is displayed means the data model survives a UI framework rewrite, which is exactly what the web port required.
- PrepareRecipeForFour puts scaling logic inside the workflow function, not inside a particular screen's widget code. Business logic tied to a specific screen cannot be reused when a second screen needs the same behavior; logic that lives in the domain layer can.
- The web port is described as needing to produce equivalent outcomes through shared rules and data contracts, not a visual clone of the Android app. A port or a second client is honest when it reimplements the platform-appropriate parts and reuses the platform-independent parts, rather than copying pixels.
Key terms, in plain language
You do not need a systems background to follow the work. These are the specialized terms used on this page.
- Dependency injection
- Providing a component with the services it needs from outside rather than constructing them internally. This makes dependencies visible and permits focused replacements in tests or alternate platforms.
- Repository boundary
- An interface that describes domain operations while hiding where and how data is stored. Screens can request recipe or list behavior without becoming coupled to Firestore details.
- Reactive stream
- A continuing sequence of values that updates subscribers when state changes. ShoppyCat uses streams so lists, pantry items, recipes, and household data can update the interface without manual refreshes.
- Cloud Firestore
- A document database that can synchronize data and notify connected clients when documents change. Security Rules independently decide which authenticated operations are allowed.