9

Looking at the clean architecture layers and flow diagrams, and implemented it my self in my applications, I've always wondered which layer is supposed to contain the DB, or any 3rd Party service or SDK.

Looking at both of these images raises the question if there isn't violation in the outer layers.

enter image description here

enter image description here


I've imagined the layers division like this:

enter image description here

But this means that there is a violation of the dependancy rule. Since the gateway always knows about both the external service, and the application it self, the entities.

Is there a correct way to draw these layers? I've read a couple of resources asking this question, but didn't really get a full answers to what I need. For example: https://groups.google.com/g/clean-code-discussion/c/oUrgGi2r3Fk?pli=1, Doesn't repository pattern in clean architecture violate Dependency inversion principle?

I get it that the meaning of clean architecture is kept, and the inner layers, the entities and the use case, aren't affected by a change in the DB and the gateway, but was just wondering if maybe this is more accurate:

enter image description here

edit:

From the book:

Recall that we do not allow SQL in the use cases layer; instead, we use gateway interfaces that have appropriate methods. Those gateways are implemented by classes in the database layer.

So I guess this means that the data access is really in the most outer layer:

enter image description here

Maybe for this specific example, there is no real use for the interface adapters layer?

But also from the book about interface adapters layer:

Similarly, data is converted, in this layer, from the form most convenient for entities and use cases, to the form most convenient for whatever persistence framework is being used (i.e., the database). No code inward of this circle should know anything at all about the database. If the database is a SQL database, then all SQL should be restricted to this layer—and in particular to the parts of this layer that have to do with the database.

Also in this layer is any other adapter necessary to convert data from some external form, such as an external service, to the internal form used by the use cases and entities.

So it kinda contradicts that the data access is in the database layer, since this is what it does, converts from the DB, for example SQL rows, into the application's entities. Are these layers not really separated? I'm confused.

oren
  • 257
  • 2
  • 6
  • 2
    I'm struggling with this a bit myself. I think the thing missing from nearly all architectural diagrams is the role that data transfer objects play in decoupling the layers. – Greg Burghardt Oct 17 '20 at 14:17

3 Answers3

2

Frameworks and Drivers. The outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework, etc. Generally you don’t write much code in this layer other than glue code that communicates to the next circle inwards.

This 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.

cleancoder.com - the clean architecture

So you do, in fact, write code in the blue layer. The the DB aware code goes where it says DB. Across the boundary though, what DB?

And that means, no, you do not run dependencies both ways across a boundary.

I'd put it in a different way. Think of your gateway as a contract (like Java interface). User of this contract, something sitting in more inner ring is defining what is needed. And on contract level there should be nothing that would require a specific technology or any implementation details. Of course, there needs to be an implementation of this contract, where you can put all the details like those DB structure or SQL queries.

Jacek Bilski - clean-code-discussion

So the gateway doesn't know it's talking to a DB. But it knows the needs of the application.

When I see "DB" in the outer ring of the clean architecture diagram I don't imagine the actual DB. I imagine the only DB aware code. After all, this is a diagram of an object graph. Not a system architecture.

Done this way, so long as whatever is plugged into your gateway can support its needs it doesn't matter if it's a DB, a file system, or system ram.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • 1
    "I imagine the only DB aware code" - I mean, it doesn't matter, that just pushes the application boundary one more layer away; the DB aware code is at the edge, and is dependent on the DB, making the layer dependent on both sides (unless you treat it as a wrapper and consider DB dependence an internal detail). I think that's what the OP is asking about - what happens at the edge. – Filip Milovanović Oct 18 '20 at 17:17
  • 1
    @FilipMilovanović the simple answer is that the “boundary” between the physical DB and the code that is aware of it is not depicted in the diagram. Uncle Bob is continuing a long tradition of keeping silent about things you might like to know but that haven’t quiet been nailed down. – candied_orange Oct 18 '20 at 17:22
  • He's also contradicting himself a bit (I think); e.g. on the cleancoder.com, under Interface Adapters he says "If the database is a SQL database, then all the SQL should be restricted to this layer, and in particular to the parts of this layer that have to do with the database." - and having SQL in there is a form of dependence, although, SQL being widely supported I suppose it's a at a lower level of coupling, than say, anything that references concrete framework code (establishing a connection, framework config, that sort of stuff) - I suppose that would be the Frameworks and Drivers layer. – Filip Milovanović Oct 18 '20 at 17:34
  • But I guess that the main takeaway is that at some point in the outermost layers you have to reference external stuff. – Filip Milovanović Oct 18 '20 at 17:34
  • @FilipMilovanović yes, but that still doesn't mean crossing a boundary in two directions. It means doing [this](https://softwareengineering.stackexchange.com/questions/368521/im-struggling-to-see-how-dependency-inversion-doesnt-lead-to-tighter-coupling/368525#368525) – candied_orange Oct 31 '20 at 12:53
  • But that picture is exactly the same (topologically) as the one I drew in my answer to this question :D. Just take my BusinessObject to be your App. I think we mean fundamentally the same thing, but perhaps we're communicating it in different ways. BTW, I didn't say that a boundary is crossed in both directions, I said that the outermost layer depends on both sides (over two boundaries), except that I pointed out that the outer boundary is qualitatively different. – Filip Milovanović Oct 31 '20 at 13:08
  • @FilipMilovanović but that is what the OP asked. It is a point worth being clear on. – candied_orange Oct 31 '20 at 13:12
  • It occurred to me that I may have missed this point, and, you're right, it's definitely something worth addressing, but after re-reading the question, I'm not sure the OP has that particular misconception. The two-way boundary only appears in the edited image, and this may be just because the OP edited it in a hurry; throughout the rest of the text the OP never explicitly talks about a two way boundary, but is mostly asking about the direction of the dependency at the very edge, w/ resp. to the dependency rule. But, yeah, it's probably worth emphasizing. – Filip Milovanović Oct 31 '20 at 13:26
  • @FilipMilovanović what's bugging me most is I remember a blog or talk that depicted multiple hexagonal architectures that talked to each other and were arranged in a honey comb pattern. It dealt with this exact issue. I'm having no luck finding it again. – candied_orange Oct 31 '20 at 13:29
  • Hm... that sounds vaguely familiar, maybe I've seen it. Do let me know if you happen to find it. – Filip Milovanović Oct 31 '20 at 13:40
1

Yes, the (internal) implementation of the outermost layer depends on an external system (database, service, library).

You've said:

So I guess this means that the data access is really in the most outer layer:
enter image description here
Maybe for this specific example, there is no real use for the interface adapters layer?

The Data Access object (which may be a single class, or a collaboration of a couple of objects) is an interface adapter - it adapts the interface provided by the database (e.g., SQL queries) into the Data Access Interface that the Interactor requires, allowing the Interactor to remain unaware of database details. The conceptual problem is that the database itself is external to the application1, so the dependency on it crosses a different type of boundary. You can think of it in a couple of different ways (see below).

interface adapter

Basically, there are two ways to think about this. One is to take the outermost classes and the external system together; this combo is then just "the external world", that depends on an interface in an internal layer. This conceptually preserves the dependency rule (you're "wrapping" the external dependencies, and the arrow pointing outside is then an internal detail of the wrapper).

Alternatively, you can think of the dependency rule as being confined to the boundaries of your system, and allow the stuff that's on the very boundary to depend on external APIs.

This is mostly a matter of preference, and the way you think about it doesn't have any real impact.

Framework and Drivers

Regardless of what the Frameworks and Drivers layer actually represents in Bob Martin's diagram2, note that frameworks and drivers themselves are external software - you don't have control over them (generally speaking) - which is what makes the application boundary qualitatively different from a layer boundary.

This doesn't pertain to standard libraries and types associated with the language, as these are the basic building blocks of the application and are referenced throughout all of your code, so all of it depends on these (this is not depicted on the diagram).


1The database is external in the sense that it's a separate system not written by you (the tables and stored procedures may be, but not the database itself), so you don't have direct control over its interface in the same way (or to the same extent) you have across layers, for the code you own. On a related note, in a distributed system (say, a collection of interacting services), you can still think about the architecture and dependencies in a similar way (it's just a different deployment model), but the nature of the boundaries may change (different teams may be responsible for different services, team power dynamics and politics come into play). So in that scenario, even though you're, ostensibly, working on the same distributed system, you have varying degrees of control over its constituent elements.

2It may just be an extra layer containing some glue code highly specific to an external framework/library/driver. After all the number of layers in Clean Architecture is not fixed.

Filip Milovanović
  • 8,428
  • 1
  • 13
  • 20
1

I've been struggling with this for some time as well. The way I now interpret the dependency direction between the Gateway part (green) and the DB (blue) is as follows.

Imagine you are using an ORM such as Hibernate or Entity Framework. You will implement your persistence in the gateway part using Entity Framework without having to think about the actual Database. That's the code you write.

At the same time, Entity Framework itself will have different implementations to transform the set of operations into actual SQL instructions depending on the Database. That part of Entity Framework is what I would consider to be in the blue part of the dependency circle. In addition, the part of your code that configures Entity Framework to use, for example Microsoft SQL server would belong to that blue section too.

I think in many cases this differentiation might not exist, and the dependency circle might stop at the gateway level.

enter image description here

Edit: I've included a diagram that might help better explain my interpretation. The blue part would correspond to specific SQL, config and drivers to your database. The green part would be more how you want to store the entity. For example:

Your entity could be a purchase order with all the line items. You can store that in two different tables one for the purchase order and other for all the line items that belong to that order. So far this can be still independent of your actual database. Now the blue part would consist of the actual SQL statements for your specific DB. That would also include specific drivers, settings and SQL scripts that are bound to your specific DB.

From my point of view this could be a very thin line and we could consider all that just the persistence gateway itself and don't have the blue section at all.

David
  • 11
  • 3