Welcome to another deep dive. Today we're taking a plunge into
something pretty fundamental in software engineering, Clean
Architecture. Yeah, it comes up a lot.
It really does. And it's not just, you know, a
buzzword. It's a real approach to building
software that's robust, adaptable.
Our guide today is Software Engineering a Modern Approach by
Marco Tulio Valente. Specifically Chapter 7, right?
The one on building with clean architecture.
Exactly. And of course, you can't really
talk about clean architecture without mentioning Robert Martin
or Uncle Bob as most people know him.
True, he really championed this philosophy.
And the goals listed in the source?
They're ambitious, high reusability, strong cohesion,
technology independence and great testability.
It sounds like the Holy Grail of software design.
Almost. It does.
So the big question for you listening in is probably OK,
how? How does 1 architectural pattern
deliver all of that? What's the secret sauce here?
Well, it's a great question and fundamentally clean
architecture. It's a layered architecture, a
sophisticated 1, sure, but layers are key.
It's designed specifically to make software resilient,
adaptable. And those diagrams, you always
see the concentric circles. Yeah, those circles are actually
crucial. They're not just decoration,
they visually represent the core principles, how the separation
works. Strategic separation, OK, I like
that. So to get it, we need to start
right at the center. Yeah, the absolute core.
Exactly. Go right to the middle, that
innermost layer. That's where the most stable
parts of your application live. The core business concerns.
The real essence of what the software is for.
Like totally separate from any specific tech.
Precisely. And in that core, there are
basically two kinds of classes. First, you've got entities.
OK, These are classes often shared across multiple systems
in an organization. Think about university example
from the source. Entities would be things like
student, instructor, course, department.
Things that lots of different parts of the university system
would need to know about, right? And importantly, they don't just
hold data, they also implement generic business rules.
The source gives the example. Maybe a rule like every
instructor must belong to exactly 1 department.
That's a core business fact. Got it.
Not tied to how you store instructors, just a.
Rule exactly. Then still in that inner circle
you have use cases. Use cases.
OK, these classes handle the specific operations for this
system's domain. Sticking with the university use
cases might be things like creating a new class, enrolling
a student, cancelling an enrollment, maybe recording
subjects covered in a lecture. So the actions, the verbs kind
of to the entities nouns. That's a good way to put it.
These are the specific processes the system enables.
So these central layers, I mean, this is where the real value,
the intellectual property lives, right?
Free from all the messy details of databases or I don't know,
front end frameworks. Exactly, Imagine trying to
change a core policy and finding it tangled up with sequel
statements. It's a common headache.
Clean Architecture keeps these rules pure insulated.
You define the business first, then worry about tech.
OK, That makes a lot of sense. So moving out one layer from
that core, we get to the adapters, what role do they
play? They sound like middle men.
They are crucial intermediaries, maybe skilled translators is
good analogy. They manage the conversation
between the inner core and the outside world.
So adapters are classes and interfaces that well they adapt.
They mediate between the external parts like UIS,
databases, external services and that central business logic, the
use cases and entities. So take a system with a REST API
that the source mentions this. The adapters would be the
classes implementing those API endpoints.
They get a request, figure out which use case needs it and pass
it. On and then handle the response
coming back. Exactly.
They take the result from the use case, maybe format it as
Jason, and send it back to the front end.
It ensures the core logic doesn't need to know anything
about Http://orjason. Like skilled translators, as you
said, making sure everyone understands each other even if
they speak different technical languages, keeps the core clean.
Precisely, it protects that core.
OK, so that naturally leads us to the outermost layer, the edge
of the circles. This is where What did the
source say all the details go? That's the phrase, and it's very
apartment. This outer layer is home to
classes from libraries, frameworks, external systems,
all the specific technologies you're using.
So like. Database drivers, E-mail
library. Exactly.
Databases, e-mail services, payment gateway integrations,
maybe talking to specific hardware.
The university example mentioned using a third party credit card
provider for extension courses, right?
The class this is for that specific provider would live out
here. They're details from the core
business perspective. Uncle Bob in his own book really
hammers this home. He says the frameworks and
drivers layer is where all the details go.
The web is a detail, the database is a detail.
We keep these things on the outside where they can do little
harm. Little harm.
I like that it implies they could do harm if they were mixed
in with the core logic. They absolutely could.
Changes in those details could ripple inwards and break things.
So the big take away here is your core business logic.
Those entities and use cases, they just don't care.
SQL no SQL, send grid, mail gun doesn't matter to the core.
Correct. That core is insulated,
protected. Focus purely on the business
rules. That's how you get that
technology independence. You can swap out details more
easily. That sounds incredibly powerful
for future proofing, but OK, this strict separation, how is
it actually enforced, especially if, say, a use case needs to
trigger something in an outer layer?
This leads us to the dependency rule, right?
Yes, the dependency rule is absolutely fundamental.
It's the bedrock principle that makes this architecture work.
So what is it exactly? Simply put, code in an inner
layer cannot reference anything in an outer layer.
No direct dependencies pointing inwards.
Nothing. No classes functions.
Nothing. Uncle Bob is very clear.
Nothing in an inner circle can know anything at all about
something in an outer circle. In particular, the name of
something declared in an outer circle must not be mentioned by
the code. In an inner circle that
includes, yeah, functions, classes, variables, anything
named. Wow, that's strict.
It has to be. The benefit is huge.
It makes those inner layers, the entities and use cases,
incredibly stable. They don't change just because
you decided to switch database vendors or adopt A new
JavaScript framework. Because they literally don't
know about them. Exactly.
The business logic is shielded from technological churn.
You avoid those cascading changes where updating a library
forces changes deep in your core logic.
OK. So that's what really keeps the
core clean. It's this strict dependency
management discipline is key it sounds like.
It definitely requires discipline.
But then the obvious question arises, what if an inner layer,
like a use case for enrolling a student, needs to do something
that involves an outer layer, like sending a confirmation
e-mail? The e-mail service logic is way
out there in the details layer. A direct call is forbidden by
the rule. Right, direct calls would break
the architecture. So how do you solve that?
How does the inside talk to the outside without violating the
rule? This is where the elegance of
Inversion of Control comes in, usually implemented with
interface. In version of control.
OK, how does that work here? So instead of the use case
directly calling say a mail service simple class in the
outer layer. Which you can't do which.
You can't do. Instead you define an interface
right there in the inner use case layer, let's call it Mail
Service interface. It just defines what needs to be
done, like a method send recipient message.
So the use case only knows about this abstract interface defined
right next to it. Precisely the use case depends
only on this abstraction. Mail service interface, then way
out in the external frameworks layer.
Your concrete mail service simple class implements that
mail service interface. I see.
So the dependency arrow is flipped.
The outer layer implementation now depends on the inner layer
interface. Exactly.
The control of which implementation gets used is
inverted. The inner layer defines the
contract, the interface. The outer layer provides the
implementation. The source has a class diagram
showing this flow, how the dependencies point inwards
towards abstractions. That is genuinely clever because
the use case can trigger the e-mail sending via the
interface, but it has zero knowledge of how it's sent or
which service is doing it. The separation is maintained.
Perfectly maintained. You could swap mail service
input for another mail service input tomorrow, and as long as
it implement the mail service interface, the use case layer
doesn't change. Not one line of code.
That's the real power, adaptability without rewriting
your core logic. OK, so we've walked through the
layers, the crucial dependency rule and this smart inversion of
control solution. Let's zoom out.
Why go through all this effort? What's the big picture benefit
for you, the listener, The developer?
Well, it connects directly back to those core software
engineering concepts we always talk about and that the source
emphasizes. Clean architecture isn't just
some isolated idea, it's strongly promotes things like
high cohesion within layers, loose coupling between layers,
and a very clear separation of concerns makes sense.
It also heavily relies on principles like single
responsibility, each layer, each class having a clear purpose and
as we just saw dependency inversion and we talked about
the adapter pattern being used explicitly.
So it brings together a lot of best practices.
It really does, and the source boils down the recommendations
quite nicely. First, start by defining your
entities, those core data structures and generic rules.
Right, the shared stuff. Then focus on the use cases this
specific business operations for this application, but keep them
totally technology agnostic. Remember the web is a detail,
the database is a detail. Keep.
Chanting that mantra. Pretty much.
And finally, design the adapter classes, those intermediaries
that handle the translation between the pure core and the
messy outside world. Entities use cases, then
adapters focus inward, then build the bridges outward.
That's a great way to summarize it.
And if you follow that path, the ultimate benefit is an
architecture that cleanly separates business interests
from technology interests. Which means which means your
system becomes way easier to test.
You can test core logic without databases or UIS.
And crucially, it's far easier to adapt to new technologies
down the road. You're not locked in.
So less pain when the next big framework arrives or when you
need to switch cloud providers or database types.
Exactly. It's about building for change,
building for the long term. It's about protecting your core
business value from the turbulence of technology trends.
Easier testing, easier adaptation.
That's the promise, and when done well, Clean Architecture
delivers. It sounds like it's about
building software that embraces change, not fights it.
That's it perfectly. In today's tech landscape, which
just keeps evolving faster and faster.
Building software that embraces change, well, it isn't just good
practice anymore, It's pretty essential for longevity.
A really powerful idea to keep in mind.
Thank you for joining us on this deep dive into Clean
architecture today. Hopefully you feel a bit more
equipped to think about these layers and dependencies in your
own projects.