{"id":19522186,"url":"https://github.com/captaincodeman/clean-go","last_synced_at":"2025-05-08T16:54:46.346Z","repository":{"id":43146390,"uuid":"64044006","full_name":"CaptainCodeman/clean-go","owner":"CaptainCodeman","description":"Clean Architecture Example in Go","archived":false,"fork":false,"pushed_at":"2017-05-06T02:05:04.000Z","size":3488,"stargazers_count":280,"open_issues_count":4,"forks_count":25,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-05-07T04:37:47.365Z","etag":null,"topics":["appengine","application-architectures","clean-architecture","database","datastore","mongodb"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CaptainCodeman.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-24T01:56:02.000Z","updated_at":"2025-02-24T18:47:56.000Z","dependencies_parsed_at":"2022-08-27T11:10:23.511Z","dependency_job_id":null,"html_url":"https://github.com/CaptainCodeman/clean-go","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CaptainCodeman%2Fclean-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CaptainCodeman%2Fclean-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CaptainCodeman%2Fclean-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CaptainCodeman%2Fclean-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CaptainCodeman","download_url":"https://codeload.github.com/CaptainCodeman/clean-go/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253112072,"owners_count":21856070,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appengine","application-architectures","clean-architecture","database","datastore","mongodb"],"created_at":"2024-11-11T00:37:35.529Z","updated_at":"2025-05-08T16:54:46.275Z","avatar_url":"https://github.com/CaptainCodeman.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Clean Architecture in Go\nAn example of \"Clean Architecture\" in Go to demonstrate developing a testable\napplication that can be run on AppEngine with Google Cloud Storage or with \ntraditional hosting and MongoDB for storage (but not limited to either).\n\nThere are a number of different application architectures that are all simlar\nvariations on the same theme which is to have clean separation of concerns and \ndependencies that follow the best practices of \"the dependency invesion principle\":\n\nA. High-level modules should not depend on low-level modules. Both should depend on abstractions.\n\nB. Abstractions should not depend on details. Details should depend on abstractions.\n\nVariations of the approach include:\n\n* [The Clean Architecture](https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html) advocated by Robert Martin ('Uncle Bob')\n* Ports \u0026 Adapters or [Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture) by Alistair Cockburn\n* [Onion Architecture](http://jeffreypalermo.com/blog/the-onion-architecture-part-1/) by Jeffrey Palermo\n\nFrom more in-depth practical application of many of the ideas I can strongly \nrecommend the excellent book [Implementing Domain-Driven Design](http://www.amazon.com/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577)\nby Vaughn Vernon that goes into far greater detail.\n\nBesides the clean codebase, the approaches also bring other advantages - significant\nparts of the system can be unit tested quickly and easily without having to fire \nup the full web stack, something that is often difficult when the dependencies \ngo the wrong way (if you need a database and a web-server running to make your \ntests work, you're doing it wrong).\n\nI'd used it before in the world of .NET but forgot about it after moving to coding\nmore in Python. After switching languages again (yeah, right) to the wonderful \nworld of go I came across a blog post which re-ignited my interest in it:\n[Applying The Clean Architecture to Go applications](http://manuel.kiessling.net/2012/09/28/applying-the-clean-architecture-to-go-applications/)\n\nIt's a great read but I found the example a little overly-complex with too much of\nthe focus on relational database model parts and at the same time it was light\non some issues I wanted to resolve such as switching between different storage types\nand web UI or framework (and Go has so many of those to chose from!).\n\nI've also been looking for a way to make my application usable both standalone\nand on AppEngine as well as being easier to test, so this seemed like a good opportunity\nto do some experimenting and this is what I came up with as a prototype which I've\nhopefully simplified to show the techniques.\n\n## Dependency Rings\nWe've all heard of n-tier or layered architecture, especially if you've come \nfrom the world of .NET or Java and it's unfair that it gets a bad rep. Probably\nbecause it was often implemented so poorly with the typical mistake of everything\nrelying on the database layer at the bottom which made software rigid, difficult\nto test and closely tied to the vendor's database implementation (hardly surprising\nthat they promoted it so hard).\n\nReversing the dependencies though has a wonderful transformative effect on your \ncode. Here is my interpretation of the layers or rings implemented using the Go \nlanguage (or 'Golang' for Google).\n\n### Domain\nAt the center of the dependencies is the domain. These are the business objects\nor entities and should represent and encapsulate the fundamental business rules\nsuch as \"can a closed customer account create a new order?\". There is usually a\nsingle root object that represents the system and which has the factory methods \nto create other objects (which in turn may have their own methods to create others). \nThis is where the domain-driven design lives.\n\nLooking at this should give you an idea of the business model for the application\nand what the system is working with. This package allows code such as unit tests \nto excercise the core parts of the app for testing to ensure that basic rules are \nenforced.\n\n### Engine / Use-Cases\nThe application level rules and use-cases orchestrate the domain model and add richer\nrules and logic including persistence. I prefer the term engine for this package \nbecause it is the engine of what the app actually does. The rules implemented at this\nlevel should not affect the domain model rules but obviously may depend on them. \nThe rules of the application also shouldn't rely on the UI or the persistence \nframeworks being used.\n\nWhy would the business rules change depending on what UI framework is the new flavour \nof the month or if we want to change from an RDBMS to MongoDB or some cloud datastore?\nThose are implementation details that pull the levers of the use cases or are used by\nthe engine via abstract interfaces.\n\n### Interface Adapters\nThese are concerned with converting data from a form that the use-cases handle to\nwhatever the external framework and drivers use. A use-case may expect a request \nstruct with a set of parameters and return a response struct with the results. The \npublic facing part of the app is more likely to expect to send requests as JSON or \nhttp form posts and return JSON or rendered HTML. The database may return results \nin a structure that needs to be adapted into something the rest of the app can use.\n\n### Frameworks and Drivers\nThese are the ports that allow the system to talk to 'outside things' which could be\na database for persistence or a web server for the UI. None of the inner use cases \nor domain entities should know about the implementation of these layers and they may \nchange over time because ... well, we used to store data in SQL, then MongoDB and \nnow cloud datastores. Changing the storage should not change the application or any \nof the business rules. I tend to call these \"providers\" because ... well, .NET.\n\n# Run\nWithin app folder ...\n\n## App Engine\nInstall the AppEngine SDK for Go:\n\n    goapp serve\n\n## Standalone\nStart mongodb and build / run the go app as normal:\n\n    mongod --config /usr/local/etc/mongod.conf\n    go run app.go\n\n## Run Tests\nNot yet added\n\n    ginkgo watch -cover domain\n    go tool cover -html=domain/domain.coverprofile\n\n# Implementation Notes\n\n## Build tags\nGo has build tags to control which code is included and when running on AppEngine\nthe `appengine` tag is automatically applied. This provides an easy way to include\nor exclude code that will only work on one platform or the other. i.e. there is no\npoint building the appengine provider into a standalone build and some code can't\nbe executed on appengine classic - this provides a way to keep things separated.\n\n## Dependency Injection\nSurely it's needed for such a thing? No it isn't. While DI can be a useful tool,\nvery often it takes over a project and becomes an entangled part of the application\narchitecture masquerading as the framework. Seriously, you don't need it and it \noften comes with a huge cost in terms of complexity and runtime performance. \nWhatever a DI framework does, you can do yourself with some factories - what we\nused before the world went DI crazy and thought Spring was a good idea (oh, how\nwe laugh about it now).\n\n## Query spec\nThe Query spec provides a way to pass a query definition to the providers in a\nstorage agnostic way without depending on any database specific querying language.\nThis was attempted in .NET with Linq to mixed results - you often ended up coding\nfor the specifics of certain databases (usually SQL server) but in this case the\nquery language is much simpler and designed to be more lightweight as it only has\nto provide some filtering capability for what is going to be a NoSQL database or\na SQL database being used in a non-relational way.\n\n## Storage providers\nI picked AppEngine Datastore and MongoDB because they are kind of similar in that\nthey are both NoSQL stores but are pretty different in how connections and state\nare maintained. The MongoDB storage has the connection passed in through the \nfactory setup. The Datastore has no permanent connection and uses the context\nfrom each request.\n\n## Enhancements\nThere's a lot missing. Some obvious things would be to pass request information\nsuch as authenticated user, request IP etc... in a standard struct embedded within\neach interactor request. The responses should also return errors that the web ui\nadapter could use in the response.\n\nA console adapter could be created to demonstrate the ability to use the engine\napp logic and the storage without the web ui. Speaking of which ... some unit tests\nwould also show how the majority of the system can be tested without having to \nfire up a web server or a database. Test storage instances can be used to test \nthe engine and test engine instances can help test the web handlers.  \n\n## What's with the imports?\nWhy do I separate the imports in Go? I just like it ... I divide the imports into:\n\nStandard packages (e.g. `strings` or `fmt`)\n\nExtensions to packages (e.g. `net/http` or `encoding/json`)\n\n3rd party packages (e.g. `google.golang.org/appengine/datastore`)\n\nApplication packages (e.g. `github.com/captaincodeman/clean-go/domain`)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaptaincodeman%2Fclean-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaptaincodeman%2Fclean-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaptaincodeman%2Fclean-go/lists"}