Try a clean architecture in Golang

Source: Internet
Author: User
Tags dsn sprintf
This is a creation in Article, where the information may have evolved or changed. > (Independence, Testability and brevity) after reading Uncle Bob's Clean Architecture Concept, I tried to implement it in Golang. Our company also uses a similar architecture, [Kurio-app Berita Indonesia] (Https://kurio.co.id/), but the structure is a little different. Not too different, the same concept, but the file directory structure is different. Here you can find a sample project [Https://github.com/bxcodec/go-clean-arch] (Https://github.com/bxcodec/go-clean-arch), which is a CRUD Manage Sample Articles! [] (https://raw.githubusercontent.com/studygolang/gctt-images/master/clean-arthitecture/1_ Cytejrpihc-dfe23utlzfq.png) *  Disclaimer: I do not recommend the use of any library or framework here, you can use your own or third parties with the same functionality of any framework to replace. # # Foundation We need to understand the following constraints before designing a clean architecture: 1. independent of the framework. The architecture does not depend on the existence of some powerful repositories. This allows you to use such a framework as a tool, rather than letting your system fall into the constraints of the framework constraints. 2. testability. Business rules can be tested without a UI, database, Web service, or other external element. 3. Separate from the UI. The UI can be easily changed without changing the rest of the system. For example, the Web UI can be replaced with the console UI without changing the business rules. 4. Separate from the database. You can replace Oracle or SQL Server with Mongo, BigTable, CouchDB, or other databases, and your business rules do not bind to the database. 5. Independent of external media. In fact, your business rules can be as simple as not knowing the outside world at all. More details: [Https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html] (https://8thlight.com/blog/ uncle-bob/2012/08/13/the-clean-architecture.html) So, the baseTo these constraints, each layer must be independent and testable. Like Uncle Bob's architecture There are 4 layers: * Solid layer (entities) * Use case layer (usecase) * Control Layer (Controller) * Frame and drive layer (framework & Driver) in my project, I also used a 4-tier architecture: * Model Layer (MODELS) * Warehouse Layer (Repository) * Use case layer (usecase) * Presentation layer (Delivery) # # model layer (Models) as with entities (entities), the model is used in each layer, where it will be stored The structure of the object and its methods. For example: Article, Student, book. ' Goimport ' "time" type article struct {ID int64 ' JSON: ' id ' ' title string ' JSON: ' title ' ' Content string ' JSON: ' content ' Updatedat time. Time ' JSON: "Updated_at" ' Createdat time. Time ' JSON: ' Created_at ' '} ' so the entity or model will be stored in this layer # # Warehouse layer (Repository) warehouse will store all the database processors, query, create or insert the database processor will be stored in this layer, the layer only for the database execution CRUD operations. There is no business process for this layer. Only normal functions that manipulate the database. This layer is also responsible for selecting what database will be used in the application. Can be Mysql, MongoDB, Mariadb,postgresql, regardless of which database to use, it is decided at this level. If ORM is used, this layer controls the input and interfaces with the ORM service. If the microservices are called, they will also be processed on this layer. Create an HTTP request to request additional services and clean up the data, which must act as a repository entirely. Handles all data input, output, and does not have a specific logical interaction. The warehouse layer (Repository) will depend on the connection database or other microservices (if present) # # use case layer (usecase), which will play the role of the Orchestration processor. Any process will be handled here. This layer will determine which warehouse layer is used. and is responsible for providing the data to the service for delivery. Process the data for calculations or do anything here. The use-case layer will receive all the processed input from the delivery layer and then store the processed input in the database, or fetch the data from the database. Use case layerWill depend on the warehouse tier. # # Presentation Layer (Delivery) this layer will act as the performers. Determines how data is rendered. Any delivery type can be either a REST API or an HTML file, or the GRPC layer will receive input from the user and clean up the data and pass it to the use-case layer. For my sample project, I use the REST API as a representation. The client invokes the resource node over the network, and the presentation layer obtains the input or request and then passes it to the use-case layer. This layer relies on the use-case layer. # # # Communication between layers and layers in addition to the model layer, each layer needs to communicate through the interface. For example, the use case (usecase) layer requires a warehouse (Repository) layer, so how do they communicate? The warehouse (Repository) layer will provide an interface as a bridge for them to communicate. Warehouse Layer (Repository) interface example: ' Gopackage repositoryimport models ' github.com/bxcodec/go-clean-arch/article ' type Articlerepository Interface {Fetch (cursor string, num Int64) ([]*models. Article, error) GetByID (ID Int64) (*models. Article, error) Getbytitle (title string) (*models. Article, error) Update (article *models. article) (*models. Article, error) Store (a *models. article) (Int64, error) Delete (ID Int64) (bool, error)} ' The Use case layer (usecase) will communicate with the warehouse layer through this interface, and the warehouse layer (Repository) must implement this interface so that the use-case layer ( UseCase) Use this interface. Example of a use-case layer interface: ' ' Gopackage usecaseimport ("github.com/bxcodec/go-clean-arch/article") type Articleusecase interface {Fetch (cursor string, num Int64) ([]*article. Article, string, error) GetByID (ID Int64) (*arTicle. Article, error) Update (AR *article. article) (*article. Article, error) Getbytitle (title string) (*article. Article, error) Store (*article. article) (*article. Article, error) Delete (ID Int64) (bool, error)} "is the same as the use case layer, and the presentation layer will use this contract interface." and the use-case layer must implement the interface. # # Test We know that brevity means independence. Even in the absence of other layers, each layer has testability. * Model (Models) Layer This layer tests only functions or methods declared by any struct. This can be done independently of the other layers and easily tested. * Warehouse (Repository) layer in order to test this layer, the better way is to do the integration test, but you can also do a simulation test for each test, I use Github.com/data-dog/go-sqlmock as my tool to simulate the query process mysql* use cases ( UseCase) layer because this layer relies on the warehouse layer, it means that the tier requires a warehouse layer to support testing. So we made a mock warehouse (Repository) model based on the contract interface we defined earlier. The Delivery layer is the same as the use case layer, because the layer relies on the use-case layer, which means changing to a use-case layer to support testing. The use-case layer is also simulated based on the contract interfaces that were previously defined. For simulation, I use Vektra's Golang simulation library: [Https://github.com/vektra/mockery] (Https://github.com/vektra/mockery) # # Warehouse Layer ( Repository) test in order to test this layer, as I said before, I use Sql-mock to simulate my query process. You can use github.com/data-dog/go-sqlmock like me, or use other libraries with similar features. "' Gofunc Testgetbyid (t *testing. T) {db, mock, err: = Sqlmock. New () if err! = Nil {t.fatalf ("An error '%s ' is not expected when opening a stub database connection", err)} defer db. Close () Rows: = Sqlmock. Newrows ([]string{"id", "title", "Content", "Updated_at", "Created_at"}). AddRow (1, "Title 1", "Content 1", time.) Now (), time. Now ()) Query: = "Select Id,title,content,updated_at, created_at from article WHERE id = \ \?" Mock. Expectquery (query). Willreturnrows (rows) A: = articlerepo.newmysqlarticlerepository (db) num: = Int64 (1) anarticle, err: = A.getbyid (num) asse Rt. NoError (t, err) assert. Notnil (t, anarticle)} ' # # use case layer (usecase) tests sample tests for use-case layers, depending on the warehouse layer. "' Gopackage usecase_testimport (" Errors "" StrConv "" Testing "" Github.com/bxcodec/faker "models" github.com/bxcodec/ Go-clean-arch/article "" Github.com/bxcodec/go-clean-arch/article/repository/mocks "UCase" github.com/bxcodec/ Go-clean-arch/article/usecase "" Github.com/stretchr/testify/assert "" Github.com/stretchr/testify/mock ") func Testfetch (t *testing. T) {Mockarticlerepo: = new (mocks. Articlerepository) var mockarticle models. Articleerr: = Faker. Fakedata (&mockarticle) assert. NoError (t, err) Mocklistartilce: = MakE ([]*models. Article, 0) Mocklistartilce = append (Mocklistartilce, &mockarticle) mockarticlerepo.on ("Fetch", mock. Anythingoftype ("string"), mock. Anythingoftype ("Int64")). Return (Mocklistartilce, nil) u: = UCase. Newarticleusecase (mockarticlerepo) num: = Int64 (1) Cursor: = "A" list, nextcursor, err: = U.fetch (cursor, num) cursorexpected: = StrConv. Itoa (int (mockarticle.id)) assert. Equal (t, cursorexpected, Nextcursor) assert. Notempty (t, Nextcursor) assert. NoError (t, err) assert. Len (t, List, Len (mocklistartilce)) mockarticlerepo.assertcalled (T, "Fetch", mock. Anythingoftype ("string"), mock. Anythingoftype ("Int64")} "mockery will generate a warehouse layer model for me, I do not need to complete the warehouse (Repository) layer first, I can complete the use case (UseCase), Even though my warehouse (Repository) layer is not yet implemented. # # Presentation Layer (Delivery) test performance layer tests depend on how you pass the data. If you use the HTTP REST API, we can use the built-in package httptest in Golang. Because this layer relies on the use case (usecase) layer, we need to simulate usecase, which is the same as the warehouse layer, and I use mockery to simulate my usecase for the performance layer (Delivery) test. "Go func testgetbyid (t *testing. T) {var mockarticle models. Article err: = Faker. Fakedata (&mockarticle) assert. NoError (t, err) Mockucase: = new (mocks. articleusecase) num: = Int (mockarticle.id) mockucase.on ("GetByID", Int64 (num)). Return (&mockarticle, nil) e: = Echo. New () req, err: = http. Newrequest (Echo. GET, "/article/" + StrConv. Itoa (num), strings. Newreader ("")) assert. NoError (t, err) Rec: = Httptest. Newrecorder () c: = E.newcontext (req, rec) c.setpath ("Article/:id") c.setparamnames ("id") c.setparamvalues (StrConv. Itoa (num)) handler:= articlehttp.articlehandler{ausecase:mockucase, helper:httphelper.httphelper{}} handler. GetByID (c) assert. Equal (t, http. Statusok, rec. Code) mockucase.assertcalled (T, "GetByID", Int64 (num))} "# # Final output and merge after all layers have been encoded and passed the test. You should merge it into a system in the Main.go file of the root project. Here you will define and create each of the environmental requirements and merge all the layers together. Take my main.go as an example: "Gopackage mainimport (" Database/sql "" FMT "" Net/url "Httpdeliver" github.com/bxcodec/go-clean-arch/ Article/delivery/http "Articlerepo" Github.com/bxcodec/go-clean-arch/article/repository/mysql "ArticleUcase" Github.com/bxcodec/go-clean-arch/article/usecase "CFG" githUb.com/bxcodec/go-clean-arch/config/env "" Github.com/bxcodec/go-clean-arch/config/middleware "_" github.com/ Go-sql-driver/mysql "Github.com/labstack/echo") var config cfg. Configfunc init () {config = cfg. Newviperconfig () if config. Getbool (' Debug ') {FMT. Println ("Service RUN on DEBUG mode")}}func main () {dbhost: = config. GetString (' Database.host ') dbport: = config. GetString (' Database.port ') dbUser: = config. GetString (' Database.user ') dbpass: = config. GetString (' Database.pass ') dbName: = config. GetString (' database.name ') connection: = Fmt. Sprintf ("%s:%s@tcp (%s:%s)/%s", DbUser, Dbpass, Dbhost, Dbport, DbName) val: = URL. Values{}val. ADD ("Parsetime", "1") Val. ADD ("Loc", "Asia/jakarta") DSN: = FMT. Sprintf ("%s?%s", Connection, Val. Encode ()) dbconn, err: = SQL. Open (' MySQL ', DSN) if err! = Nil && config. Getbool ("Debug") {FMT. PRINTLN (Err)}defer dbconn.close () E: = Echo. New () Middl: = middleware. Initmiddleware () e.use (middl.cors) ar: = articlerepo.newmysqlarticlerepository (dbconn) au: = ARTICLEUCASE.NEWARTICLEusecase (AR) httpdeliver.newarticlehttphandler (E, au) e.start (config. GetString ("Server.address")} "you can see that each layer is merged with its dependencies. # # Conclusion In short, if you draw on a picture, as shown:! [] (https://raw.githubusercontent.com/studygolang/gctt-images/master/clean-arthitecture/1_ gqdkad7iwiwoww-wlg5ikq.png) * Every library you use here can be modified by yourself. Because the focus of the compact architecture is that the libraries you use are not important, the key is that your architecture is concise, testable, and Independent. * My project is organized in this way. By commenting and sharing, you can discuss or agree, and of course it's better to improve it. # # Sample Project Sample project can be seen here: [Https://github.com/bxcodec/go-clean-arch] (Https://github.com/bxcodec/go-clean-arch) Libraries used in my project: * Glide: Package management tool * Go-sqlmock from github.com/data-dog/go-sqlmock* testify: Test library * Echo Labstack (Golang Web framework) for Presentation Layer * Viper: Environment Configuration Further reading concise architecture: * [https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html] (https:// 8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html) * [http://manuel.kiessling.net/2012/09/28/ Applying-the-clean-architecture-to-go-applications/] (http://manuel.kiessling.net/2012/09/28/ applying-the-clean-architecture-to-go-applications/). This is a neat architecture for another version of Golang. If you have any questions, or needMore explanations, or I don't have a clear explanation here. You can contact me via my [LinkedIn] (https://www.linkedin.com/in/imantumorang/) or [email] (iman.tumorang@gmail.com). Thank you.

via:https://hackernoon.com/golang-clean-archithecture-efd6d7c43047

Author: Iman Tumorang Translator: fredvence proofreading: polaris1119

This article by GCTT original compilation, go language Chinese network honor launches

This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove

783 Reads
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.