Trouble squaring the advice for Interface and Adapters & Frameworks and Drivers

146 views
Skip to first unread message

Terence Kent

unread,
Jan 23, 2023, 1:39:27 PM1/23/23
to Clean Code Discussion
👋 Hello!

Long time listener, first time caller. I've got a nagging question that I'm finally asking about. Hopefully, it's something straightforward that I've missed.

I've long been confused about some of the advice in Clean Architecture around the Interfaces and Adapters layer and the Framework and Drivers layer. There seems to be two pieces that you can't really follow at the same time. I can best explain things with an example, so here goes.

Imagine a trivial project with a single use case for receiving a "thingy". We'll use a layout where each layer is put in its own directory to be explicit. The first two layers look like this:
Screen Shot 2023-01-22 at 5.01.49 PM.png

Here the ReceiveShipmentInteractor takes some input, does some entity operations, and uses the ThingyPersister interface to persist. Everything is simple so far.

Next we come to the Interfaces & Adapters layer. If we assume we'll ultimately use a MySQL DB and we take this quote about the layer literally...

 |  If the database is a SQL database, then all SQL should be restricted to this layer.
(p.205)

Then it sure seems like you want to write an implementation of the ThingyPersister interface in the Interfaces & Adapters layer with all the SQL statements. Maybe a DataAccessor interface is added to avoid driver coupling and you end up with something like this: 

Screen Shot 2023-01-22 at 5.07.33 PM.png

But then that leaves us struggling with the advice on the Frameworks and Drivers layer.

 | The frameworks and drivers layer is where all the details go, The web is a detail. The database is a detail...
(p.205)

Huh... If the database is just a detail, then it's hard to square the advice of adding the SQL in the interfaces and adapters layer. At the least, you are forcing knowledge of the category of database (SQL). And, of course, the situation is worse that that for SQL databases. The SQL you write for, say MySQL and the MSFT SQL Server, are ever-so-slightly different for many features. If you are using a document database, things would be even more vendor-specific.

That points to implementing ThingyPersister in the Frameworks and Drivers layer. Say, a MysqlThingyPersister. The diagram a few pages later sure seems to back up this idea...

index2.png
(p208, figure 22.2)

If I assume that the double lines in the books picture are layer boundaries, then the four layers are pretty easy to identify and the EntityGateway interface (in the use case layer) is NOT implemented in the interface adapters layer, but in the Frameworks and Drivers layer.

Then, we would have this...
Screen Shot 2023-01-22 at 5.13.13 PM.png

Unfortunately if we do that, we've not followed the advice on page p205 and we've "skipped a layer" between interface and implementation.

The only solutions I can imagine to this situation bring in their own problems. One option is to have an additional set of DTOs and new interface at Interface and Adapters layer. Perhaps having it look like:

Screen Shot 2023-01-22 at 5.45.18 PM.png
This will work to avoid the layer skipping. However, no SQL is being placed in the Interface and Adapters layer which violates the advice. And, the amount of code placed in frameworks and drivers is a much larger, which is not supposed to be the case.

Another option is to bring the vendor-specific knowledge into the Interfaces and Adapter layer, and leave the specific driver for interacting with the vendor DB out. That might look a bit like this.
Screen Shot 2023-01-22 at 5.50.49 PM.png
Of course, now a lot of detail about the specific database just came up from the frameworks and drivers layer, voilating other advice.

So, how are these pieces of advice followed at the same time? In most projects where I end up using these layers, I end up deciding to put vendor-specific implementations in the Interface and Adapters layer, but I am pretty sure that's not what's recommended.

Thanks!
Terence

Sam Stone

unread,
Feb 26, 2023, 3:27:56 AM2/26/23
to Clean Code Discussion
Fascinating!

Łukasz Duda

unread,
Feb 26, 2023, 7:50:55 AM2/26/23
to Clean Code Discussion
Hi Terence,
I put ORM / SQL queries into interface adapters layer. These adapters convert format convenient for use cases to format required by database engine.
Here you have the examples:
  1. Format convenient for the use case: the gateway interface.
  2. The interface adapter: the repository implementation. Uses EF Core to adapt business request to SQL query.
I guess in the second example EF Core and SQL Server database connection belong to frameworks and drivers. "Generally you don’t write much code in this layer".

Regards
Łukasz

deepbl...@gmail.com

unread,
Feb 26, 2023, 11:08:56 AM2/26/23
to Clean Code Discussion
Hi Terence.

You seem to have a slight misunderstanding. The double lines are the boundaries of your APP. It's more like a rectangle encompassing those layers. Everything inside the rectangle is your app. Everything outside the rectangle is the outside world. And yes, UI is part of the outside world. 
There is no "interfaces and adapters" layer. At least not in the way you seem to think.
There are Use-cases, Entities and Gateways!
In your case, you have a persistence gateway. This gateway has an interface, that takes/returns entities. How those entities are serialized and persisted, and where, are the details that the book talks about, and they are completely locked away into the gateway implementation. All your app sees is the interface of the gateway, which mentions nothing about SQL. Hence why its interface is on the "border" of your app app and the implementation is in the outside world. If you want to split the gateway implementation into a bunch of collaborating classes, like DBManager (driver) Serializer, Cache, etc, you can but again, the app knows nothing about these details. 
Hope this helps.

Norbert

Prashant Choudhary

unread,
Feb 26, 2023, 12:56:25 PM2/26/23
to Clean Code Discussion
What usually worked for me is to have two interfaces.
1. Is a general storage/repository to store and retrieve Thingy. This is agnostic to tech and closer to how domain needs it.
2. Have another interface that the repository would depend on to avoid direct dependency on Storage drivers. I tend keep this as optional as switching DB is often a significant choice in itself and the interface often doesn't add much value. But if that is very probable then this separation helps with refactoring and testability.

Terence Kent

unread,
May 11, 2023, 5:03:58 AM5/11/23
to Clean Code Discussion
Hey Lukasz,

Sorry for the delayed response! I've done that as well, so it's nice to have the confirmation. That nagging problem that I ways have with that approach is what you pointed out. ORMs, like like EF Core (or hibernate on the JVM, or whatever) feel like a straightforward framework. So it feels like I'm violating the advice. 

Best,
Terence

On Sunday, February 26, 2023 at 4:50:55 AM UTC-8 Łukasz Duda wrote:

Terence Kent

unread,
May 11, 2023, 5:03:58 AM5/11/23
to Clean Code Discussion
Hey Norbert,

I'm trying to follow along with your comment, but I'm not sure we're talking about the same things. Maybe we are and I'm just missing something!

I see no conflict at all with the "core parts" of any app needing to be ignorant of the outside world. The inner two layers (entities + use cases) just define interfaces for what they need from the outside. That's all well and good and easy to understand. It's a repeated theme in the DDD and CQRS designs, which all tend to have something like (domain + app) with the same general theme.

The part I'm honing in on is the two outer rings shown in the diagram on page 203 and outlined on page 205 (confusingly, the layer names are written in the circles for entities and use cases and off the annotation lines for the outer two. However, they are explicitly named on the following pages).
1_B7LkQDyDqLN3rRSrNYkETA.jpg

Using the diagram above, say a use case needs to do something like persist an entity or write a result back to a caller. Just like you said, it should define an interface for it's needs and expect some-unknown-to-it implementation to be provided. You can have this situation with no logical conflict or fuss if you just merge the outer two layers broadly into "infrastructure" or "outside". Honestly, that's typically what I do.

My question is focused on "is it even possible to follow the advice?" I gave an example around database implementations, but the same scene shows up for all sorts of other things. If your position is "the interface and adapters & framework and drivers layers don't really exist - they are more of ideas. Just keep your app pure and details on the outside", then sure. That works, it just means the answer to my post is "you can't strictly follow the advice" and that's a fine outcome.

I'm raising it here more because I'm curious if I've just been missing a path to implementing it. Maybe it's always been right under my nose.

Best,
Terence

Norbert Nemes

unread,
May 11, 2023, 12:30:47 PM5/11/23
to clean-code...@googlegroups.com
Hi Terence!

Sure. The problem I have with that diagram is that while it’s technically correct, it has too much information and confuses users. It did the same to me. That’s why I attended Uncle Bob’s clean architecture seminar, where I asked him all my questions 🤣.

So it is best to imagine the architecture as 3 layers.
Gateways
Use cases (+ entities)
UI (+ presenters)

Wherever they touch, you have interfaces.

So between the UI and use cases you have one that accepts some data structures to be passed to the use case to do its job, and outputs the result to the presenter, that generates a presentable something for the UI to use to populate its fields. That way the UI can be completely dumb. The presentable and “some data structure are NOT the entities.

Between use case and gateways you have another interface that take entities to be sent outside the system and returns entities that were generated based on data that comes into the gateways. At no point does the use case later know anything about networking, authentication, database schemas, or that there is even a database. All that logic is locked into the gateway.
Your app is just the use case layer, entities and the interfaces for the gateway UI. That’s what the double lines encompass. 

UI, Presenters, gateway implementations are all on the outside if your app.

I highly recommend you read Uncle Bob’s eponymous Clean Architecture book. He goes into more details there.

In your case, not sure you have a UI but let’s assume you want to list let’s say the contents of a cart.
That is your use case listCart.
Entities? Cart + product
UI? Whatever you use, web controller, command line whatevs. Presenter, you can take the output ds, let’s call it partialProduct and run it through a presenter to create a presentableProduct with all fields probably strings, properly formatted for presenting them to the end user.
So from the UI layer you call the use case, that will immediately call the gateway ti fetch the contents of your cart through the gateway interface.  The gateway will talk to the database, make an SQL query, construct a Cart entity which contains product entities, and returns them to the ise case. The use case takes these, and creates the partial product/cart data structures and sends them out to the UI layer. Note that entities are NOT just data structures. They have behaviors. Like for instance Cart has functions to list all products, calculate the total value of the products, tax on it, everything cart related that wouldn’t change if you moved it to another shopping app. The partialCart/product stuff are just dumb data structures. No logic there.

Can you create more collaborator classes? SURE! You can have a query generator for the sql, that talks to the DB manager, that sends it out and the result is returned to the parser that generates the entities. You might also have a humble object that wraps the actual system api calls, that does wonders for testing.

It’s the art of drawing lines.

Hope this helps.

Sent from my iPhone

On May 11, 2023, at 2:03, Terence Kent <teren...@gmail.com> wrote:

Hey Norbert,

I'm trying to follow along with your comment, but I'm not sure we're talking about the same things. Maybe we are and I'm just missing something!

I see no conflict at all with the "core parts" of any app needing to be ignorant of the outside world. The inner two layers (entities + use cases) just define interfaces for what they need from the outside. That's all well and good and easy to understand. It's a repeated theme in the DDD and CQRS designs, which all tend to have something like (domain + app) with the same general theme.

The part I'm honing in on is the two outer rings shown in the diagram on page 203 and outlined on page 205 (confusingly, the layer names are written in the circles for entities and use cases and off the annotation lines for the outer two. However, they are explicitly named on the following pages).
--
The only way to go fast is to go well.
---
You received this message because you are subscribed to a topic in the Google Groups "Clean Code Discussion" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clean-code-discussion/kWyzEaJznv4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clean-code-discu...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/clean-code-discussion/2bdbecce-4776-46d6-ac23-752a33f5d30en%40googlegroups.com.
<1_B7LkQDyDqLN3rRSrNYkETA.jpg>
Reply all
Reply to author
Forward
0 new messages