Designing for FoodBasket
Hear me rant about the process of designing my capstone project, while people that have no clues about actual designs scream at me for not doing it well enough.
I noticed a lot of clean architecture specifications don't have any demonstration on what it would look like in practice. Taking the chance of learning it in a course of Software Architecture, I think I would like to write up a demo just like so!
Published on Apr 30, 2025
Updated on May 1, 2025
As you may recall, we have gone over Clean Architecture by Uncle Bob during one of the lectures, and as of that, our assignment is to provide a (very simple) demonstration of said architecture in any of our projects.
Clean Architecture by Uncle Bob splits the software into 4 separate layers:
A few notes about the architecture is that:
Let’s keep it simple. We want to keep track of Mario’s stats (including HP, heart points, also known as health in other games, and FP, flower points, also known as mana or elixir in other games). Mario will be our only entity here.
class Mario (
var hp: Int,
var maxHp: Int,
var fp: Int,
var maxFp: Int,
)
For the sake of the example, we’ll be setting up two simple use cases, to even start setting up the battlefield system for Mario, we need two preliminary system use cases: GetMarioStatus and CreateMario, respectively allowing us to see what Mario’s stats are at and being able to create a Mario object.
class CreateMarioUseCase {
fun execute(hp: Int, fp: Int): Mario {
return Mario(hp, hp, fp, fp)
}
}
class GetMarioStatusUseCase {
fun execute(mario: Mario) = buildString {
append("${mario.hp}/${mario.maxHp}HP")
append(" ")
append("${mario.fp}/${mario.maxFp}FP")
}
}
Notice, how our return data types and parameters are only those data types known to the use case layer, which is only the entities layer’s names and functions.
In this layer, I created a MarioController which allows us to basically “control” Mario, in a real development project, this would also consist of letting Mario attack, defend, use items and flee from battle, etc. If you’re working with a Spring Boot application for example, the controller would be the code calling up business methods with data retrieved from parameters and bodies.
There’s also a MarioPresenter, which also allows us to view and present Mario’s data, essentially converting data from the use case layer to something that an outside driver can understand.
class MarioController {
private lateinit var mario: Mario
fun createPresenter() = MarioPresenter(mario)
fun createMario() {
val createMarioUseCase = CreateMarioUseCase()
mario = createMarioUseCase.execute(10, 5)
}
}
class MarioPresenter(val mario: Mario) {
fun getMarioStatus(): String {
val getMarioStatusUseCase = GetMarioStatusUseCase()
return getMarioStatusUseCase.execute(mario)
}
}
Notice, that in our controllers and presenters, we invoke use cases instead of creating or accessing Mario’s Data on our own. The reason for this is that usually if you have a need to switch out a layer, as long as these use cases hold on to the same signatures, you can change the implementation or configuration as much as you want.
The final outermost layer here, I simulated with the Console as the “UI” part of the application, mainly with the println function from Kotlin. The issue here is that the println function only understands String, it shouldn’t have to mind what the Mario object is and how to manage it. For a Spring Boot Application, this usually is the view engine, as you need to parse the data properly so the view engine can understand.
private lateinit var marioController: MarioController
private lateinit var marioPresenter: MarioPresenter
fun setup() {
marioController = MarioController()
}
fun start() {
println("Setting up Mario Controller")
marioController.createMario()
marioPresenter = marioController.createPresenter()
println("Getting Mario Status")
println(marioPresenter.getMarioStatus())
}
Here in layer 4, we don’t care about the details below, we just care that we can have a controller to act with Mario, and the presenter to retrieve the data in a way that the UI (basically Strings for println) can understand.
Hear me rant about the process of designing my capstone project, while people that have no clues about actual designs scream at me for not doing it well enough.
Registering and Logging in with user accounts, along with session tokens for authorization stored in HTTP cookies, made possible with CORS (CORS is a pain).
My first steps into my biggest project yet, a full-stack application for a comic-sharing website like Wattpad, Webtoon, etc. My first time ever setting up the server on a self-hosted VPS.