University project · Team of 4
DAE - Full-Stack Architecture & AI
A business application where every service runs in its own container and the AI model runs on the machine itself, without data ever leaving the infrastructure.
Context
A project for the Enterprise Application Development course, in the third year of my degree. The data model was given by the lecturer; everything else we built.
The problem
Building a business application in Java with role-based access control and AI features, under one requirement that shaped everything else: the environment had to be reproducible by anyone, on any machine, without local installs and without "it works on my computer".
My part
My part was the frontend. I ended up building and fixing much of the backend as well.
Technical decisions
Full containerisation, no exceptions
Backend, database, frontend and the AI model — each in its own container, connected over an internal network. The alternative was installing dependencies locally and documenting the process in an instructions file. I chose containers because an instructions file goes stale in two weeks and a single command does not. The cost is a slow first start and one more layer to debug when something fails.
A local AI model instead of an external API
Calling a commercial API would have been faster to integrate, cheaper in hardware, and would have produced better answers. I ran the model locally anyway, for three reasons: the data never leaves the infrastructure, which in a business context is often a requirement rather than a preference; the cost per request is zero, which changes the economics of any frequently used feature; and the application works with no internet connection. The cost is real and I do not hide it — slower answers, lower quality than a commercial model, and a hardware requirement an HTTP call does not have. In a consumer-facing application I would have chosen the API. In a business application handling customer data, I would make this choice again.
Emails intercepted in the test environment
The application sends notifications by email. In development, those emails are captured by a test server instead of going out into the world. It is a small precaution with a large consequence: no real address ever receives a test email, and it remains possible to check exactly what would have been sent.
Keeping part of the backend and rewriting the rest
Part of the backend was already built when I realised it would not do — structural decisions that could not carry the permission requirements. With a deadline running, the choice was between throwing it all away and starting over, or judging it piece by piece. I chose the second: I kept what was correct and rebuilt what was not. Throwing everything away is quicker to decide and wastes work that was fine; keeping everything is more comfortable and drags the problems to the end. The middle path cost more to decide, and it is what made delivery possible.
What went wrong
I found out too late that part of the backend was not going to work. Not because anyone hid it, but because I only looked when I needed to use it — I assumed it was progressing because nobody had said otherwise.
When I opened the code, I found structure that could not support what the permission system needed, and there was no longer time to start over. I had to judge each part separately and decide what to save and what to redo, with the clock running.
The lesson is not about code. It is that on a team, progress is assumed until it is seen, and seeing costs little when it is early. Had I looked at that code two weeks sooner, the decision would have been the same and the cost would have been half.
Outcome
A working application, with a published demo and the entire environment starting from a single command.
What I'd do differently
Review everyone's work early and regularly, instead of waiting for the moment I need it. Not out of distrust — because finding a problem early costs a conversation, and finding it late costs a weekend.