1

I am currently trying to follow the clean architecture approach but i wonder where common things like a database connections should take place.

Since i think a database connection usually will be opened once, but different tables in there are used from feature to feature in different ways, it wouldn't make much sense to implement (even as a singleton pattern) for this in every feature that will be developed.

But the almost every example (at least for my current flutter application) i found is exactly doing this. The database instance is being created in a feature itself.

Especially when looking at a library like Drift, that is generating only one common file for the database connection and the used tables, its really hard for me to get it to the right place.

Were should something common like the database connection should go into? Indeed in every feature? In the core space?

Jim Panse
  • 338
  • 3
  • 11
  • To get it to "the right place?" What is the "right place," and why is it hard for you to get "it" there? – Robert Harvey Apr 19 '23 at 00:05
  • The right place for me is somewhere where i can access it from anywhere else, since every feature somehow need the database connection itself but indeed not the same tables .. therefore its hard for me considering a new data base connection in every single feature ... maybe i also do net get the idea of clean architecture right, where db connections sit on the same level like web and UI – Jim Panse Apr 19 '23 at 11:15
  • See [this diagram](https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg). If you want something to be accessible from any ring, you have to put it in the center circle. The inner rings can't access anything in the outer rings (the Dependency Principle), so putting it anywhere else will exclude it from some your features. – Robert Harvey Apr 19 '23 at 11:35
  • Why DB is then put on the outer circle when it should be accessible from the entities? – Jim Panse Apr 19 '23 at 11:38
  • That's a good question. I never much understood a lot of Uncle Bob's teachings. But the principles are clear: if you want it to be accessible from anywhere, it has to go into the center circle. – Robert Harvey Apr 19 '23 at 11:38
  • For me it reads a bit like UI -> controller -> usecase -> entities -> use case -> controllers -> db ... but no implementation i saw does use a usecase to access db from an entity ... instead, DAO+DB is used more or less directly from there... i am confused ... – Jim Panse Apr 19 '23 at 11:40
  • Presumably, in Bob's diagram, the DB's in question are not required to be accessible from the inner rings. – Robert Harvey Apr 19 '23 at 11:40
  • Generally speaking, you wouldn't access a DB from an entity. You would use the entity as a DTO. The data access would occur elsewhere. – Robert Harvey Apr 19 '23 at 11:41
  • Where on UI -> controller -> usecase -> entities -> use case -> controllers -> db at the moment going back from entities to use case we have to apply dependency inversion ... whereas to the entity, we do not have to use it – Jim Panse Apr 19 '23 at 11:42
  • Which means what, exactly? – Robert Harvey Apr 19 '23 at 11:42
  • However, calling anything from entities layer would be possible only using use cases ... never saw that dependency in any implementations ... – Jim Panse Apr 19 '23 at 11:44
  • My guess is you're building a business application. In my opinion, clean architecture is a bit overwrought. Take from it what is useful to you, and leave the rest. Ask yourself: do all of these layers provide any business value, in terms of maintainability or performance? If the answer is unclear, then maybe you don't need clean architecture. Maybe you just need UI -> controller -> database. – Robert Harvey Apr 19 '23 at 11:45
  • For example, the one-way dependencies are quite a useful principle, and can be applied anywhere, especially MVC. The interactors and output ports? Not so much. – Robert Harvey Apr 19 '23 at 11:47
  • 1
    Also, keep in mind that a lot of modern architectures, including this one, are designed for **very large** applications, written by teams of developers. If your application is not Facebook or Youtube, you may not need this at all. – Robert Harvey Apr 19 '23 at 11:49
  • 1
    My advice is to *simplify.* – Robert Harvey Apr 19 '23 at 11:50
  • Thats imo a good thought, but i always want to understand why and were it could be applied, but i feel i don't get the whole idea at all ... if dependency inversion is important to get the dependecys in the right directions does it also means we don't need interfaces for any inward pointing dependency? For example a presenter can be directly injected in the UI without any interface abstraction? I thought, interfaces are generally a good idea to decouple from implementations – Jim Panse Apr 19 '23 at 11:55
  • I don't understand why you think dependencies being in a particular direction precludes the use of interfaces. DI doesn't have anything to do with the direction that dependencies flow. Can you provide an example of what you mean? – Robert Harvey Apr 19 '23 at 11:58
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/145439/discussion-between-robert-harvey-and-jim-panse). – Robert Harvey Apr 19 '23 at 11:58
  • Code base doesn't have to be large. Just have a need for parts to be independently deployable. Being large just kinda forces you to have that need. – candied_orange Apr 21 '23 at 15:08

2 Answers2

2

I am currently trying to follow the clean architecture approach but i wonder where common things like a database connections should take place.

Right here:

enter image description here

Since i think a database connection usually will be opened once, but different tables in there are used from feature to feature in different ways, it wouldn't make much sense to implement (even as a singleton pattern) for this in every feature that will be developed.

A "feature" isn't an architectural thing in Clean Architecture. Sorry but it just isn't. The closest Clean comes is the use case. Package by feature is a good idea. It's just someone else's idea.

That said, you can be feature centric and still use Clean Architecture. Heck, each feature can have it's own onion.

But the almost every example (at least for my current flutter application) i found is exactly doing this. The database instance is being created in a feature itself.

Which is fine. But it's also not something Clean Architecture speaks to. Clean Architecture is a dependency diagram. It shows you what statically knows about what. It doesn't show you how to build it. Just what it should look like when you're done.

enter image description here github.com - esakik

Can you look at that and tell me if Data Access is a long lived object that was built in main() (Who needs singletons?) or something that the Use Case Interactor had built just now? Nothing in these diagrams answers that question. So you're free to do it either way and can still claim you're following Clean Architecture.

Why DB is then put on the outer circle when it should be accessible from the entities? Jim Panse

enter image description here

The DB is accessible from the entities. Remember, this is a dependency diagram.= Not a data flow diagram.== Those black arrows going into the circles don't mean this is a roach motel that traps you inside.

enter image description here

Uncle Bob tried to make that clear with the little "Flow of control" diagram in the lower right corner. Notice Presenters dependency arrow points to Use Case Output Port but the Flow of control goes exactly the other way. That's what they call dependency inversion.== Because of that, control can dive into a lower layer and come back out. All without using return!

And if you can do that, you can do this:

enter image description here crosp.net

So don't feel like you have to do this:

enter image description here slideshare.net
youtube.com - The Principles of Clean Architecture by Uncle Bob Martin

By the way, Casey Muratori is a game dev with a focus on performance who has been critical of some of Uncle Bobs teachings. You can see them hash it out here. It's a really good read and a good reminder that doing stuff this way isn't free.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
0

Reading the question and the (very good) answer of @candied_orange, I'd like to add something.

A lot of people think of a database as the most important and centralized part of an application. That's a common mistake, fed by the marketing departments of the database manufacturers. The database is not the main part, it's just a detail; the most important part of the application is the implementation of the business rules and features (i.e., the code), and the database is just something to store the data to be processed. A file storage, or a REST API storage (or plain old stone tables ;-)) could also do the job.

That's why Uncle Bob says that the database is just a detail and should be hidden behind a storage adapter or Data Access Component (DAC).

So, what is the 'right place' to put a database connector? Since we need only one database connector for one database, we should make sure that the database connector is instantiated once (especially when initializing the database takes time), but accessible for the components that need access to the database. The exact implementation depends on the language (C#, Java, dart) and frameworks used.

Frits
  • 390
  • 2
  • 8
  • There is an important caveat to this: Persistence is often the slowest part of the application, and also commonly the location of concurrency. A plain file storage would get corrupted the first time two instances of the application attempted to persist different versions of the same entity, and would be horribly slow the first time you tried to look up 1 entity in 1 million. As such, the choices made in how you do persistence can have far more significant impact on non-functional requirements like performance and data integrity than how you write your business rules. – user1937198 Apr 22 '23 at 23:52