# Context When developing my side projects, as an individual developer, I want to make releasing my app(s) on multiple platforms as easy as possible. To do this, I use Kotlin Multiplatform Mobile as this allows me to release on Android, iOS, Desktop (Windows/OSX/Linux) and finally Web. This had me thinking that I could extend this to the "backend" of my applications which brought me down the rabbit hole of Domain Driven Design. As this was not a far shot from the obsession I had with CLEAN Architecture... it was the next natural step! The goal of this article is to outline the structure I will be using across Budgey, Lift Bro, and any future applications. # Project Structure To do this I will be splitting my application(s) into 3 layers: domain, presentation and data. ## Domain This is the core of the application, it is the module that defines the feature set and objects that power the application. For Lift Bro this would mean: - Lift - Set (LBSet from here on out) - Excercise As well as interfaces that help define the methods and API that can be used to interact with the domain models. ex: ```kotlin interface class SetRepository { fun getAll(): Flow<List<LBSet>> fun save(transaction: LBSet): Boolean fun getAllByVariation(variationId: String): Flow<List<LBSet>> fun delete(setId: String): Boolean } ``` For the most part these interfaces will define CRUD operations for the other layers to implement or use as needed. This layer will be as simple as possible with no business logic and only base definitions. In theory this layer should also have 0 dependencies... but that is not always as possible as I want. ## Presentation In most cases this will be the UI that can be expanded to include other definitions of what "presentation" could mean. For now we will define this as "the layer that the user will interact with". Which in most cases will be the user interface of a website or mobile device. And the front end business logic that goes along with that (ex: validating an email in a login screen) This is where your MV* lives (MVC, MVVM, MVI..... etc etc). The presentation layer is responsible for taking the domain layer, and using it to deliver the user a wonderful experience. In many cases this involves mutating data to be more visually pleasing (ex: taking a `Date` object and converting it to "January 1st, 1901") ## Data The data layer is what implements our domain layer. This can also be called the "implementation" layer if that suits you! In the case of Lift Bro this is where the SQLDelight database is implemented and managed. This is also where we map from our database Entities to our domain models. ex: we may join tables together or create objects out of flattened fields within the database #### Data ```sql CREATE TABLE LiftingSet ( id TEXT NOT NULL PRIMARY KEY, variationId TEXT NOT NULL, weight REAL, reps INTEGER, tempoDown INTEGER, tempoHold INTEGER, tempoUp INTEGER, date INTEGER AS Instant NOT NULL, notes TEXT NOT NULL ); ``` #### Domain ```kotlin @Serializable data class LBSet( val id: String, val variationId: String, val weight: Double = 0.0, val reps: Long = 1, val tempo: Tempo = Tempo(), val date: Instant = Clock.System.now(), val notes: String, ) @Serializable data class Tempo( val down: Long = 3, val hold: Long = 1, val up: Long = 1, ) ``` In the above example you can see how the lifting set table itself is very flat to avoid the need for other tables, while the domain model is massaged to be more usable and structured for the consuming presentation layer! # Platform Structure ![[Pasted image 20250513194814.png]] In the above diagram we can start to see how this all works together. Since I am using Compose Multiplatform for my UI I can abstract it out of the application modules themselves. But it is valid to also create platform specific UI within the platform modules themselves! (ex: Jetpack Compose for Android, SwiftUI/UIKit for iOS.... whatever you choose for Desktop) It is then up to the platform modules to depend on and inject the implementation details as required # Introducing... The Server Exploring how to expand this even more I thought about now also including some sort of server to fetch this information Note: In all implementations Android/iOS/Desktop all locally store their data and never hit external servers. This means that a potential offsite server/locally hosted server that utilizes the same domain layer is possible! This means we need to create two things: 1. A new "application" layer (Equivalient to Android/iOS/Desktop) representing a Server 2. A new "data" layer equivalent to the SQLDelight layer that instead fetches our data from an outside source vs a local database 1. I will differentiate these by calling one the "Remote" datasource and another the "Local" datasource This means that utilizing this Domain centric system we can re-organize our structure like so ![[Pasted image 20250513194835.png]] In the above we can see a few changes. The "platform" becomes a bit smaller as our fully shared code also becomes smaller. The Server has no need for UI so the presentation layer starts to get closer to implementation details of a platform vs something that is part of the platform itself. Note: these could still be considered part of the platform if wanted! For the sake of this article I am shifting it out. This means that each time we "build the application" we get to pick between what we want! ### Example 1: The Android App ``` Server Presentation Domain RemoteData LocalData ``` And optionally enable the remote/local datasources based on the requirements of the current application (ex: a user could choose to use their phone as a remote server for editing their database) or the android application could be a client that reaches out to other services! ### Example 2: the Desktop App ``` Server Presentation Domain RemoteData LocalData ``` The desktop app could also be the same! Allowing for extreme customization for any user of the platform itself ### Example 3: The Server ``` Server Domain LocalData ``` In this example we could include these modules and create a hostable, completely separate from UI logic, domain driven, server. This could be hosted on a server, or even on something as simple as a raspberry Pi (note: I have not tried this yet... but it shoould work depending on server load/complexity). This empowers the consumer of the platform to host the platform if they decide! By being able to pick and choose which modules they wish to integrate with based on their need