👋 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:
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:
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...
(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...
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:
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.
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