We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.

--

christiansakai • 11 months ago

Thank you! This is the best code and post written about Clean Architecture. Thanks for explaining your thought process as well!

Preslav Rachev • 1 year ago

This a comprehensive and well-thought article, which I happen to have found 7 years after it was still written, and it still makes a lot of sense.

I am primarily a Java developer who has recently started professionally looking at Go. As a Java developer, this kind of thinking about splitting code into manageable components is near and dear to my heart. I do appreciate Go's lean back and less-BS culture, but I think that not enough is being discussed about applying architecture patterns to the language. Many of those are being mocked at, as some kind of Java-jibberish. As a result, inexperienced developers picking up Go often write code without a particular direction or keeping any principles of logical separation.

P.S. I read a few of the comments down below, and although some of them might have a point, no architecture design is perfect, or should be taken as the one and only. The important thing is to formulate a principle in your head.

mark jumaga • 1 year ago

Having been involved with impossible to follow spaghetti golang code at fortune 500 organizations this architecture is the answer to many if not all of the questions of where to put a specific piece of code and how to define it's functionality.
The diagram with the onion layers and ladders between the layers shows the security inherent in this design standard.
Excellent.

alex ramey • 2 years ago

I noticed the two Add method implementations in the usecases layer don't actually add the item to the order.

flo f • 2 years ago

At the first glance the article is useful but then it turns out it's full of contradictions, and with all those I'm not sure how much value it adds over the original "Clean Architecture" article. Plus the author doesn't answer the most important questions, like the one by Sam Jones below about the OrderInteractor being defined in the Interfaces layer. It's a pity because the article was very promising.

Guest • 2 years ago
Guest • 2 years ago
Manuel Kiessling • 2 years ago

Glad I could help! ;-)

Yi-Wen Chang • 2 years ago

Hi, great post! Thanks for your detailed introduction about the clean architecture.

1.
For the interface layer of the database, I usually think it should only have straightforward CRUD methods.
Let's say the method FindById of DbUserRepo in your implementation actually does 2 sql queries (getting user and then getting customers).

When I am developing things in usecase layer, how would I know that calling interactor.UserRepository.FindById actually calls 2 sql queries?

And what if I only want to check if the user is admin (meaning I don't want the second query getting customers), do I need to implement another FindByIdSimple method? (imo, this method should be called FindById, and your implementation is actually FindByIdWithCustomer)

2.
If I want to use sql transaction, what will you suggest me to do?
I only comes out 2 solutions.

i) Expose transaction into usecase layer so we can call "Insert A", "Update B", "Delete C" in usecase layer with transaction. But the downside is that we break the dependency rule. Now usecase know we're using somet persistent mechanism with transaction

ii) Provide a "InsertAandUpdateBandDeleteC" in the interface layer of DBRepo. But the downside is we are putting business logic to the interface layer (it should lives in usecase layer) and it's very difficult to scale (imagine later on we need another method for UpdateAandInsertB, then we need to implement another one)

I don't know which one is more like a clean architecture pattern and hope you can give me some advices.
Thanks for reading this.

Sági-Kazár Márk • 2 years ago

I don't think there is anything wrong with exposing the fact that you operate in a transaction. Transaction itself does not expose what sort of gateway you use.

Think about withdrawing money from an ATM: transaction is a first class citizen in that domain model.

Sam Jones • 3 years ago

Great post! One thing I'm trying to understand is why the OrderInteractor interface is defined in the interfaces layer. Since the implementation is defined in the use cases layer, doesn't this violate the dependency rule? I realize Go allows you to do this without explicitly referencing the interface type, but it seems like even an implicit dependency would violate the rule. Shouldn't the use case layer contain both the interface and the implementation?

Manuel Kiessling • 3 years ago

Hi Sam,

thanks for bringing this up. It don't think it is a violation. Quoting from Uncle Bob's definition:

“…that source code dependencies can only point inwards. 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
functions, classes, variables, or any other named software entity.”

So, the outer layer interfaces is "allowed" to look into the inner layer usecases. On the other hand, the outer layer actually must define the contract - that is, the API of code it uses from another layer - it expects for its own wiring to be able to work.

Rich • 4 months ago

I also agree that putting OrderInteractor interface in the interface layer is incorrect. As noted, the UseCase layer is looking outward. Also as noted, any change to the OrderInteractor interface would necessarily impact the UseCase layer. E.g., adding "Delete" to the OrderInteractor interface would mean that the implementation of OrderInteractor in UseCases would cause a compile error which would require a change. This is specifically what a "Clean Architecture" seeks to avoid.

More generally, let's take a snippet from the Uncle Bob quote above - "...the name of something declared in an outer circle must not be mentioned by code in an inner circle". This is exactly what is being done here. The implementation of OrderInteractor in UseCases explicitly uses/mentions names from Interfaces, the outer circle, specifically "Items" and "Add".

It would be nice if you would update the article to reflect this or
better explain why you think the article, as currently written, is
correct.

All that said I really like this article! It's well written and is the best article I've found so far that has useful, concrete, examples of the concepts in the original "Clean Architecture" article. Well done.

alex ramey • 2 years ago

I agree that the code, as written, violates the dependency inversion principle. You've basically derived the OrderInteractor interface defined in interfaces from the OrderInteractor struct implementation that exists in the usecases layer. Conceptually (as Sam mentions), the struct is conforming to this interface that it shouldn't have knowledge of since the interface is in an outer layer.

It would be cleaner if the interface were declared alongside the struct, and the repository depended on an interface from usecases.

The current code would also be better if it just did away with the interface and the interfaces layer repository just depended on the exported struct from usecases layer, but then you sacrifice mocking capabilities and make testing harder.

Sam Jones • 3 years ago

In the case of the repositories, though, it's the inner layer that defines the contract. The domain layer defines the OrderRepository interface and the interfaces layer provides the DbOrderRepo implementation.

If this were Java, in order to implement the OrderInteractor interface defined in the interfaces layer, the use cases layer would have to explicitly "mention" a name from an outer circle. Again, Go allows us to do this implicitly, but the inner circle still has to "know" something about the outer circle. If you were to make a change to the OrderInteractor interface in the outer layer, you would have to make a change to the implementation in the inner layer.

tamosius • 3 years ago

I find it interesting how golang people are touting about no need for include files like C++ does, and here we are kinda re-creating those include files in a form of interfaces.
Take this as a random thought, cause we do extensively use interfaces so that we could mock functionality from different layers of the app.

Guest • 3 years ago
Manuel Kiessling • 3 years ago

If you look at src/interfaces/repositories.go, you'll see that the repositories are fully implemented. They are interfaces in the Clean Architecture sense, that is, they form an interface between our use cases and the infrastructure. This is not to be mixed up with "interface" in the classical OOP sense!

Eduard • 3 years ago

Graphical visualization of the code from this article:
http://rumba.net.ru/images/...

Benjamin • 3 years ago

Hello ! Thanks for this awesome post, very helpful !
I was wondering where should we put the configuration? I have been using viper for a while now, and I thought putting it Infrastructure and Interfaces but where am I supposed to init all the configuration inputs?
Also, do you think it's better to inject configuration to handlers (webservices) so they can pass the proper configuration values along to use cases?

Thanks a lot in advance :)

Vl Pa • 4 years ago

I believe there is a typo. In the sentence " This way, the use cases layer can refer to a concept of the domain layer", it should say "... refer to a concept of a interface layer..". Because repositories are part of interface layer, not domain layer.

Manuel Kiessling • 3 years ago

Thanks for your feedback. However, the sentence is correct: "This way, the use cases layer can refer to a concept of the domain layer - repositories - while using only the language of the domain layer. Still, the actual code executed is in the interfaces layer."

Emre Kzd • 5 years ago

Thanks for the great article. I've been applying clean architecture to Go applications for a while(both professionally and for my hobby project).

The only remark that I have is (Kluyg also mentioned this) when you abstract the DB layer for simple operations like Store or FindById it looks nice and appealing but if you had more complex requirements (complex queries, transactions etc) it would become a huge investment to do this right.

Think about this; in a larger application that would require a high number of different types of queries to a database, you would end up with a DbUserRepo that implements tens of different functions. This is because you want your repo operations to be atomic, and have the logic implemented using those subset of operations in your domain and usecases layers.

As you go towards more complex persistence requirements from "basic persistence operations" you would find yourself trying to write a new abstraction layer for SQL in Go programming language.

My point is that I think Clean Architecture brings great convenience and clarity but implementing your application in a completely Database agnostic way is too idealistic to be practical. Different database solutions are different tools after all.

Marco Ronchese • 4 years ago

I am, like Xackery, also studying these concept and I am encountering the same problems. Keeping the domain so atomic is really expensive. Each usecase have to manually perform several db query without using joins or more complex queries. Maybe this separation could be more applicable if we use non-relational dbs, but this is contrary to database agnosticism. So far i think that Clean Arch layers are really good going toward the UI but not so much in the data side...

Xackery • 5 years ago

Emre, I am trying to learn Clean Code Architecture myself and find this question about database layering always to be difficult. I enjoy your insight, do you by chance have additional data/pointers/info I can learn more about your thoughts here?

aarongreenlee • 5 years ago

Great stuff.

Larry Clapp • 6 years ago

My name caught my eye. I had to search for a while to find where I'd given feedback at all, much less valuable. Thanks for the acknowledgements!

Steve • 6 years ago

Well articulated and extremely helpful!
I have a question regarding the repositories dependency on each other: what drawbacks would there be to injecting the already instantiated dependent repo's, rather than constructing them using the injected handlers?
For example, DbOrderRepo depends on DbCustomerRepo and DbItemRepo - why not pass constructed versions of those in, rather than the DbHandlers which were used to construct them?
Maybe I'm missing something, but it seems to me that this would make testing simpler, reduce the code complexity in the places they're used (and currently constructed) and create more of a separation between construction/wiring of the application from its execution?

steve • 6 years ago

I realise you have commented on this in the closing paragraph, but I'm still curious to know why you think in this instance it is still simpler to use the whiteboard pattern a la the DbHandler map, rather than just creating the repos in main? Maybe it's my guice-di background getting the better of me...

Manuel Kiessling • 6 years ago

See my other comment: http://manuel.kiessling.net...

Manuel Kiessling • 6 years ago

I agree, the approach you describe makes a lot of sense. I will rewrite the code if I find the time. Thanks a lot for your feedback!

Brian Knapp • 6 years ago

This is super interesting, I have been meaning to port Obvious (a clean architecture) http://obvious.retromocha.com to go. Awesome to see that other golang devs are interested in these ideas.

tomek • 6 years ago

Thanks for great article, just one question - did you come up with a nice way for sql transaction handling in this architecture? without passing transaction struct around?

Manuel Kiessling • 6 years ago

No, but of course that's important. A good topic for the next article on Go architecture :-)

Kluyg • 7 years ago

Thank you for your post!

I have a couple of questions.

You said that we should not put business rules in the database, but consider the following (very common) situation: we need to avoid registering new users using the same email twice. This sounds like a use case to me. But this is usually implemented on the database side by having a unique constraint on email field. This is the best way to implement such requirement in my opinion - if you try to implement it inside use case layer - you will end up with race condition. Two users simultaneously try to register using the same email - the check is done concurrently and for both of them returns "OK" - they both end up with the same email in the database. If you think you can fix it with synchronization of some sort (like critical section) - the issue will come back again when you need to scale out (two web servers). Probably you can fix it with custom rule in load balancer: "if it's a registration request, send all requests with the same email to the same server" then the server will protect itself with critical section, perform the test and if the test is OK - save new user. But this sounds way too difficult. Much easier to add DB constraint and handle "unique constraint violation" error inside use case layer. But this goes against your point of having all the logic in domain layer and use case layer. What do you think about it?

I don't see the point in hiding db details from repositories. Databases are sophisticated tools created to help us store and retrieve data for our apps *efficiently*. And repositories task is to store and retrieve data. But you are limiting your repositories with such a basic set of DB methods like query and exec. What if my SQL database of choice have a method findById which works twice faster than general "select ... limit 1"? We are losing in performance to be able to switch one SQL database with the other with ease? How often will your app findById and how often will you need to switch one SQL db with the other? Of course we can add findById method to DbHandler interface. But this will happen again and again and in the end we will be in situation when we still don't use the whole power of this particular DB while we have DbHandler interface which can be easily satisfied only with this particular DB. Also even such limited DbHandler interface can already be tied to particular DB if you don't limit your SQL with ANSI SQL subset. Maybe better give full DB power to repositories right from the start?

Here is another example: you have mentioned getLastOrderedItemsThatHaveAYellowColor repository method. So you can imagine having getOrdersSortedByCategoryAndTime method. But when you try to switch to say Microsoft Azure Storage you will find that you can't sort by two fields. Or another common example - pagination. You can implement pagination using skip and limit. But when you try to switch to Amazon SimpleDB you will find that it uses token pagination and you can't implement skip/limit interface. Or if you try to switch to MongoDB - you'll find that skip is broken (very slow for big collections) and should not be used.

Don't get me wrong - I like the separation of concerns and it is really great to have such strong separation as in your example. But in my opinion we should use the right tool in the right place. We still need to separate things as much as possible for the ease of understanding and for the ease of testing, but not for the ease of changing infrastructure. We should use all the power of our infrastructure. Since if you try to abstract from your infrastructure you will end up with inefficient app still tied to your infrastructure.

Manuel Kiessling • 6 years ago

Regarding your first comment (using the unique index constraint of the database to fullfill a business requirement). I agree, it makes sense to make use of this constraint. The solution would be to have the upper layers (dbHandler, repository) react accordingly if the database throws an error, whenever the constraint is violated. This way, you can use the mechanism of the DB, but still handle the case cleanly in you application.

FraBle • 7 years ago

Great article. I miss the Flattr button ;)
One question: where does CRUD functionality belong to?

Colin Jack • 8 years ago

" The main reason is that use cases are application-specific, while domain entities are not. Imagine two applications for our shop, one that allows customers to browse and buy stuff, and another one that is used by administrators to manage and fulfill placed orders"

I definitely buy into the DDD idea of bounded contexts here, meaning that its likely those are two different models not one model with two sets of use cases on top. Apart from anything else a big advantage of that approach is you can actually have a behavioral model.

tcoopman • 8 years ago

I have some question about what to put where and why. Suppose you want to find the last x orders that have been added. I would say this is a use case? But FindById is not enough to execute this use case. How would you handle this? Add a method to the OrderRepository interface in the domain?

Actually, I often find it difficult to decide what to put where and why, do you have some additional reading material on this?

Manuel Kiessling • 8 years ago

I'm currently reading "Lean architecture" and can fully recommend it.

Regarding your question, I think that's something to add to the OrderRepository - a repository is there to "get things", no matter where from, filtered and/or ordered in the way you need it. Thus, having very specialized Repository methods like "getLastOrderedItemsThatHaveAYellowColor" are in order in my opinion.

stephanos • 6 years ago

Do you think "Lean Architecture" (isn't this about DCI?) can be applied to Go? I would love to read an article like this about it!

Kamil Kisiel • 8 years ago

rambocoder: the current ShowOrder() function is thread safe because it constructs a new slice of items by performing a database query each time. Assuming the database handles concurrent queries correctly there should be no problem with this. In general though I think you are right, you will need to have some form of synchronization around your data.

I think if every request goes out to an ACID compliant database then the DB is pretty much taking care of that aspect of it for you. It becomes more of an issue if you are managing your own data in-memory or using some other data store.

rambocoder • 8 years ago

Is your webservice threadsafe? My understanding is that ShowOrder runs in it's own goroutine so as you add more handlers that will modify and read your global state, you will not have a consistent representation of your domain data.

Manuel Kiessling • 8 years ago

Let me be perfectly honest here - this code needs a lot of attention to details like, for example, thread safety. DO NOT take the code as a boilerplate for writing web applications. Use its architectural principles as a guideline for your own code.