{"id":13619895,"url":"https://github.com/resotto/goilerplate","last_synced_at":"2025-04-05T17:06:09.751Z","repository":{"id":43586710,"uuid":"297382611","full_name":"resotto/goilerplate","owner":"resotto","description":"Clean Boilerplate of Go, Domain-Driven Design, Clean Architecture, Gin and GORM.","archived":false,"fork":false,"pushed_at":"2023-05-05T02:28:51.000Z","size":82,"stargazers_count":455,"open_issues_count":1,"forks_count":50,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-03-29T16:06:18.717Z","etag":null,"topics":["clean-architecture","domain-driven-design","gin","go","golang","gorm"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/resotto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":"resotto"}},"created_at":"2020-09-21T15:30:48.000Z","updated_at":"2025-03-18T12:24:30.000Z","dependencies_parsed_at":"2024-01-12T21:17:20.566Z","dependency_job_id":"167d5470-1a7d-4804-ac48-83083d9b7339","html_url":"https://github.com/resotto/goilerplate","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/resotto%2Fgoilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resotto%2Fgoilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resotto%2Fgoilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resotto%2Fgoilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/resotto","download_url":"https://codeload.github.com/resotto/goilerplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247369952,"owners_count":20927928,"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":["clean-architecture","domain-driven-design","gin","go","golang","gorm"],"created_at":"2024-08-01T21:00:49.916Z","updated_at":"2025-04-05T17:06:09.729Z","avatar_url":"https://github.com/resotto.png","language":"Go","funding_links":["https://github.com/sponsors/resotto"],"categories":["Go","GO"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eGoilerplate\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/resotto/goilerplate/actions\"\u003e\u003cimg src=\"https://github.com/resotto/goilerplate/workflows/test/badge.svg\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/resotto/goilerplate\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/resotto/goilerplate\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://pkg.go.dev/github.com/resotto/goilerplate\"\u003e\u003cimg src=\"https://pkg.go.dev/badge/github.com/resotto/goilerplate\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/resotto/goilerplate/issues/1\"\u003e\u003cimg src=\"https://img.shields.io/badge/chat-on%20issue-yellow\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/resotto/goilerplate/blob/master/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-GPL%20v3.0-brightgreen.svg\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Clean Boilerplate of Go, Domain-Driven Design, Clean Architecture, Gin and GORM.  \n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/19743841/93784289-cbd84200-fc67-11ea-997b-b99af8affe17.png\"\u003e\n\u003c/p\u003e\n\n---\n\nWhat is Goilerplate?\n\n- **Good example of Go with Clean Architecture.**\n- **Rocket start guide of Go, Domain-Driven Design, [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html), [Gin](https://github.com/gin-gonic/gin), and [GORM](https://github.com/go-gorm/gorm)**.\n\nWho is the main user of Goilerplate?\n\n- All kinds of Gophers (newbie to professional).\n\nWhy Goilerplate?\n\n- **Easy-applicable boilerplate in Go.**\n\nNote\n\n- Default application/test code is trivial because you will write cool logic.\n- [Public API of bitbank](https://github.com/bitbankinc/bitbank-api-docs/blob/master/public-api.md#general-endpoints), which is bitcoin exchange located in Tokyo, is used for some endpoints by default.\n\n---\n\n## Table of Contents\n\n- [Getting Started](#getting-started)\n- [go get Goilerplate via SSH](#go-get-goilerplate-via-ssh)\n- [Endpoints](#endpoints)\n- [Package Structure](#package-structure)\n- [How to Cross the Border of Those Layers](#how-to-cross-the-border-of-those-layers)\n- [Dependency Injection](#dependency-injection)\n- [How to Start with Goilerplate](#how-to-start-with-goilerplate)\n- [Testing](#testing)\n- [Naming Convention](#naming-convention)\n- [With Gochk](#with-gochk)\n- [With PostgreSQL](#with-postgresql)\n- [Feedbacks](#feedbacks)\n- [License](#license)\n- [Author](#author)\n\n## Getting Started\n\n```zsh\ngo get -u github.com/resotto/goilerplate        # might take few minutes\ncd ${GOPATH}/src/github.com/resotto/goilerplate\ngo run cmd/app/main.go                          # from root directory\nopen http://0.0.0.0:8080\n```\n\n## `go get` Goilerplate via SSH\n\n`go get` fetches GitHub repository via HTTPS by default. So you might fail `go get`:\n\n```zsh\n~  \u003e go get -u github.com/resotto/goilerplate\n# cd .; git clone -- https://github.com/resotto/goilerplate /Users/resotto/go/src/github.com/resotto/goilerplate\nCloning into '/Users/resotto/go/src/github.com/resotto/goilerplate'...\nfatal: could not read Username for 'https://github.com': terminal prompts disabled\npackage github.com/resotto/goilerplate: exit status 128\n```\n\nIf you `go get` GitHub repository via SSH, please run following command:\n\n```zsh\ngit config --global url.git@github.com:.insteadOf https://github.com/\n```\n\nAnd then, please try [Getting Started](#getting-started) again.\n\n## Endpoints\n\n- With Template\n  - `GET /`\n    - NOTICE: Following path is from CURRENT directory, so please run Gin from root directory.\n      ```go\n      r.LoadHTMLGlob(\"internal/app/adapter/view/*\")\n      ```\n- With Public API of bitbank\n  - `GET /ticker`\n  - `GET /candlestick`\n    - NOTICE: This works from 0AM ~ 3PM (UTC) due to its API constraints.\n- With PostgreSQL\n  - [NOTICE: Please run postgres container first with this step.](#with-postgresql)\n    - `GET /parameter`\n    - `GET /order`\n\n## Package Structure\n\n```zsh\n.\n├── LICENSE\n├── README.md\n├── build                                     # Packaging and Continuous Integration\n│   ├── Dockerfile\n│   └── init.sql\n├── cmd                                       # Main Application\n│   └── app\n│       └── main.go\n├── internal                                  # Private Codes\n│   └── app\n│       ├── adapter\n│       │   ├── controller.go                 # Controller\n│       │   ├── postgresql                    # Database\n│       │   │   ├── conn.go\n│       │   │   └── model                     # Database Model\n│       │   │       ├── card.go\n│       │   │       ├── cardBrand.go\n│       │   │       ├── order.go\n│       │   │       ├── parameter.go\n│       │   │       ├── payment.go\n│       │   │       └── person.go\n│       │   ├── repository                    # Repository Implementation\n│       │   │   ├── order.go\n│       │   │   └── parameter.go\n│       │   ├── service                       # Application Service Implementation\n│       │   │   └── bitbank.go\n│       │   └── view                          # Templates\n│       │       └── index.tmpl\n│       ├── application\n│       │   ├── service                       # Application Service Interface\n│       │   │   └── exchange.go\n│       │   └── usecase                       # Usecase\n│       │       ├── addNewCardAndEatCheese.go\n│       │       ├── ohlc.go\n│       │       ├── parameter.go\n│       │       ├── ticker.go\n│       │       └── ticker_test.go\n│       └── domain\n│           ├── factory                       # Factory\n│           │   └── order.go\n│           ├── order.go                      # Entity\n│           ├── parameter.go\n│           ├── parameter_test.go\n│           ├── person.go\n│           ├── repository                    # Repository Interface\n│           │   ├── order.go\n│           │   └── parameter.go\n│           └── valueobject                   # ValueObject\n│               ├── candlestick.go\n│               ├── card.go\n│               ├── cardbrand.go\n│               ├── pair.go\n│               ├── payment.go\n│               ├── ticker.go\n│               └── timeunit.go\n└── testdata                                  # Test Data\n    └── exchange_mock.go\n```\n\n### ![#fffacd](https://via.placeholder.com/15/fffacd/000000?text=+) Domain Layer\n\n- The core of Clean Architecture. It says \"Entities\".\n\n### ![#f08080](https://via.placeholder.com/15/f08080/000000?text=+) Application Layer\n\n- The second layer from the core. It says \"Use Cases\".\n\n### ![#98fb98](https://via.placeholder.com/15/98fb98/000000?text=+) Adapter Layer\n\n- The third layer from the core. It says \"Controllers / Gateways / Presenters\".\n\n### ![#87cefa](https://via.placeholder.com/15/87cefa/000000?text=+) External Layer\n\n- The fourth layer from the core. It says \"Devices / DB / External Interfaces / UI / Web\".\n  - **We DON'T write much codes in this layer.**\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/19743841/93830264-afa9c480-fcaa-11ea-9589-7c5308c291f4.jpg\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html\"\u003eThe Clean Architecture\u003c/a\u003e\n\u003c/p\u003e\n\n## How to Cross the Border of Those Layers\n\nIn Clean Architecture, there is [The Dependency Rule](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html#the-dependency-rule):\n\n\u003e This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle.\n\nIn other words, **Dependency Injection** is required to follow this rule.\n\nTherefore, please follow the next four steps:\n\n1. Define Interface\n1. Take Argument as Interface and Call Functions of It\n1. Implement It\n1. Inject Dependency\n\nHere, I pick up the example of Repository.\n\n### Repository\n\n```zsh\n.\n└── internal\n    └── app\n        ├── adapter\n        │   ├── controller.go    # 4. Dependency Injection\n        │   └── repository\n        │       └── parameter.go # 3. Implementation\n        ├── application\n        │   └── usecase\n        │       └── parameter.go # 2. Interface Function Call\n        └── domain\n            ├── parameter.go\n            └── repository\n                └── parameter.go # 1. Interface\n```\n\n1. Interface at Domain Layer:\n\n```go\npackage repository\n\nimport \"github.com/resotto/goilerplate/internal/app/domain\"\n\n// IParameter is interface of parameter repository\ntype IParameter interface {\n\tGet() domain.Parameter\n}\n```\n\n2. Usecase at Application Layer:\n\n```go\npackage usecase\n\n// NOTICE: This usecase DON'T depend on Adapter layer\nimport (\n\t\"github.com/resotto/goilerplate/internal/app/domain\"\n\t\"github.com/resotto/goilerplate/internal/app/domain/repository\"\n)\n\n// Parameter is the usecase of getting parameter\nfunc Parameter(r repository.IParameter) domain.Parameter {\n\treturn r.Get()\n}\n```\n\n3. Implementation at Adapter Layer:\n\n```go\npackage repository\n\n// Parameter is the repository of domain.Parameter\ntype Parameter struct{}\n\n// Get gets parameter\nfunc (r Parameter) Get() domain.Parameter {\n\tdb := postgresql.Connection()\n\tvar param model.Parameter\n\tresult := db.First(\u0026param, 1)\n\tif result.Error != nil {\n\t\tpanic(result.Error)\n\t}\n\treturn domain.Parameter{\n\t\tFunds: param.Funds,\n\t\tBtc:   param.Btc,\n\t}\n}\n```\n\n4. Dependency Injection at Controller of Adapter Layer:\n\n```go\npackage adapter\n\n// NOTICE: Controller depends on INNER CIRCLE so it points inward (The Dependency Rule)\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/resotto/goilerplate/internal/app/adapter/repository\"\n\t\"github.com/resotto/goilerplate/internal/app/application/usecase\"\n)\n\nvar (\n\tparameterRepository = repository.Parameter{}\n)\n\nfunc (ctrl Controller) parameter(c *gin.Context) {\n\tparameter := usecase.Parameter(parameterRepository) // Dependency Injection\n\tc.JSON(200, parameter)\n}\n```\n\nImplementation of Application Service is also the same.\n\n## Dependency Injection\n\n**In Goilerplate, dependencies are injected manually.**\n\n- NOTICE: If other DI tool in Go doesn't become some kind of application framework, it will also be acceptable.\n\nThere are two ways of passing dependencies:\n\n- with positional arguments\n- with keyword arguments\n\n### With Positional Arguments\n\nFirst, define usecase with arguments of interface type.\n\n```go\npackage usecase\n\nfunc Parameter(r repository.IParameter) domain.Parameter { // Take Argument as Interface\n\treturn r.Get()\n}\n```\n\nSecond, initialize implementation and give it to the usecase.\n\n```go\npackage adapter\n\nvar (\n\tparameterRepository = repository.Parameter{}        // Initialize Implementation\n)\n\nfunc (ctrl Controller) parameter(c *gin.Context) {\n\tparameter := usecase.Parameter(parameterRepository) // Inject Implementation to Usecase\n\tc.JSON(200, parameter)\n}\n```\n\n### With Keyword Arguments\n\nFirst, define argument struct and usecase taking it.\n\n```go\npackage usecase\n\n// OhlcArgs are arguments of Ohlc usecase\ntype OhlcArgs struct {\n\tE service.IExchange                       // Interface\n\tP valueobject.Pair\n\tT valueobject.Timeunit\n}\n\nfunc Ohlc(a OhlcArgs) []valueobject.CandleStick { // Take Argument as OhlcArgs\n\treturn a.E.Ohlc(a.P, a.T)\n}\n```\n\nAnd then, initialize the struct with keyword arguments and give it to the usecase.\n\n```go\npackage adapter\n\nvar (\n\tbitbank             = service.Bitbank{}      // Implementation\n)\n\nfunc (ctrl Controller) candlestick(c *gin.Context) {\n\targs := usecase.OhlcArgs{                    // Initialize Struct with Keyword Arguments\n\t\tE: bitbank,                          // Passing the implementation\n\t\tP: valueobject.BtcJpy,\n\t\tT: valueobject.OneMin,\n\t}\n\tcandlestick := usecase.Ohlc(args)            // Give Arguments to Usecase\n\tc.JSON(200, candlestick)\n}\n```\n\n### Global Injecter Variable\n\nIn manual DI, implementation initialization cost will be expensive.  \nSo, let's use global injecter variable in order to initialize them only once.\n\n```go\npackage adapter\n\nvar (\n\tbitbank             = service.Bitbank{}      // Injecter Variable\n\tparameterRepository = repository.Parameter{}\n\torderRepository     = repository.Order{}\n)\n\nfunc (ctrl Controller) ticker(c *gin.Context) {\n\tpair := valueobject.BtcJpy\n\tticker := usecase.Ticker(bitbank, pair)      // DI by passing bitbank\n\tc.JSON(200, ticker)\n}\n```\n\n## How to start with Goilerplate\n\nWith Goilerplate, you can start your project smoothly.\n\nFor explanation, let's create simple \"CR\" part of CRUD of following specifications with Goilerplate.\n\nSpecifications:\n- There are three entities such as Customer, Product, and Order.\n- Order aggregates Customer and Product (Order is Aggregate Root).\n- There is only one usecase to create an order.\n\nNOTICE:\n- For convenience, the minimum codes are shown here.\n- For convenience, there are no test codes in this explanation.\n\nFirst of all, please prepare .go files with following package layout.\n\n### Package Layout\n```zsh\n.\n└── internal\n    └── app\n        ├── adapter\n        │   ├── controller.go                 # Controller\n        │   └── repository                    # Repository Implementation\n        │       ├── customer.go\n        │       ├── product.go\n        │       └── order.go\n        ├── application\n        │   └── usecase                       # Usecase\n        │       └── createOrder.go\n        └── domain\n            ├── customer.go                   # Entity\n            ├── product.go                    # Entity\n            ├── order.go                      # Entity\n            └── repository                    # Repository Interface\n                ├── customer.go\n                ├── product.go\n                └── order.go\n```\n\n### Define Entities\n\nSecondly, let's create entities, Customer, Product, and Order.\n\n```go\n// customer.go\npackage domain\n\ntype Customer struct {\n\tID string\n\tName string\n}\n```\n\n```go\n// product.go\npackage domain\n\ntype Product struct {\n\tID string\n\tPrice int\n}\n```\n\n```go\n// order.go\npackage domain\n\ntype Order struct {\n\tID string\n\tCustomer Customer\n\tProduct Product\n}\n```\n\n### Define Repository Interfaces\n\nAfter defining entities, let's prepare their repositories in `domain` package.\n\n```go\n// customer.go\npackage repository\n\ntype ICustomer interface {\n\tGet(id string) domain.Customer\n}\n```\n\n```go\n// product.go\npackage repository\n\ntype IProduct interface {\n\tGet(id string) domain.Product\n}\n```\n\n```go\n// order.go\npackage repository\n\ntype IOrder interface {\n\tSave(order Order)\n}\n```\n\n### Define Usecase\n\nAnd then, let's prepare the usecase of creating order.\n\n```go\n// createOrder.go\npackage usecase\n\nimport (\n\t\"domain\"            // simplified for convenience\n\t\"domain/repository\" // simplified for convenience\n)\n\ntype CreateOrderArgs struct {\n\tCustomerID         string\n\tProductID          string\n\tCustomerRepository repository.ICustomer\n\tProductRepository  repository.IProduct\n\tOrderRepository    repository.IOrder\n}\n\nfunc CreateOrder(args CreateOrderArgs) domain.Order {\n\tcustomer := args.CustomerRepository.Get(args.CustomerID)\n\tproduct := args.ProductRepository.Get(args.ProductID)\n\torder := domain.Order{\n\t\tID: \"123\",\n\t\tCustomer: customer,\n\t\tProduct: product,\n\t}\n\targs.OrderRepository.Save(order)\n\treturn order\n}\n```\n\n### Define Repository Implementations\n\nAfter preparing the usecase, let's implement repository interfaces in `adapter` package.\n\nHowever, this part is omitted here for convenience.\n\n```go\n// order.go\npackage repository\n\nimport (\n\t\"domain\" // simplified for convenience\n)\n\ntype Order struct{}\n\nfunc (o Order) Save(order domain.Order) {\n\t// omitted here for convenience\n}\n```\n\n### Define Controller\n\nFinally, let's define controller to call the usecase of creating an order.\n\n```go\n// controller.go\npackage adapter\n\nimport (\n\t\"repository\" // simplified for convenience\n\t\"usecase\"    // simplified for convenience\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nvar (\n\tcustomerRepository = repository.Customer{}\n\tproductRepository  = repository.Product{}\n\torderRepository    = repository.Order{}\n)\n\ntype Controller struct{}\n\nfunc Router() *gin.Engine {\n\tr := gin.Default()\n\tctrl := Controller{}\n\tr.POST(\"/order\", ctrl.createOrder)\n\treturn r\n}\n\nfunc (ctrl Controller) createOrder(c *gin.Context) {\n\tcustomerID := c.Query(\"customerId\")\n\tproductID := c.Query(\"productId\")\n\targs := usecase.CreateOrderArgs{\n\t\tCustomerID:         customerID,\n\t\tProductID:          productID,\n\t\tCustomerRepository: customerRepository,\n\t\tProductRepository:  productRepository,\n\t\tOrderRepository:    orderRepository,\n\t}\n\torder := usecase.CreateOrder(args)\n\tc.JSON(200, order)\n}\n```\n\nThat's it!\n\n## Testing\n\n```zsh\n~/go/src/github.com/resotto/goilerplate (master) \u003e go test ./internal/app/...\n?       github.com/resotto/goilerplate/internal/app/adapter     [no test files]\n?       github.com/resotto/goilerplate/internal/app/adapter/postgresql  [no test files]\n?       github.com/resotto/goilerplate/internal/app/adapter/postgresql/model    [no test files]\n?       github.com/resotto/goilerplate/internal/app/adapter/repository  [no test files]\n?       github.com/resotto/goilerplate/internal/app/adapter/service     [no test files]\n?       github.com/resotto/goilerplate/internal/app/application/service [no test files]\nok      github.com/resotto/goilerplate/internal/app/application/usecase 0.204s\nok      github.com/resotto/goilerplate/internal/app/domain      0.273s\n?       github.com/resotto/goilerplate/internal/app/domain/factory      [no test files]\n?       github.com/resotto/goilerplate/internal/app/domain/repository   [no test files]\n?       github.com/resotto/goilerplate/internal/app/domain/valueobject  [no test files]\n```\n\nThere are two rules:\n\n- Name of the package where test code included is `xxx_test`.\n- Place mocks on `testdata` package.\n\n### Test Package Structure\n\n```zsh\n.\n├── internal\n│   └── app\n│       ├── application\n│       │   └── usecase\n│       │       ├── ticker.go      # Usecase\n│       │       └── ticker_test.go # Usecase Test\n│       └── domain\n│           ├── parameter.go       # Entity\n│           └── parameter_test.go  # Entity Test\n└── testdata\n    └── exchange_mock.go           # Mock if needed\n```\n\n### Entity\n\nPlease write tests in the same directory as where the entity located.\n\n```zsh\n.\n└── internal\n    └── app\n        └── domain\n            ├── parameter.go      # Target Entity\n            └── parameter_test.go # Test\n```\n\n```go\n// parameter_test.go\npackage domain_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/resotto/goilerplate/internal/app/domain\"\n)\n\nfunc TestParameter(t *testing.T) {\n\ttests := []struct {\n\t\tname                       string\n\t\tfunds, btc                 int\n\t\texpectedfunds, expectedbtc int\n\t}{\n\t\t{\"more funds than btc\", 1000, 0, 1000, 0},\n\t\t{\"same amount\", 100, 100, 100, 100},\n\t\t{\"much more funds than btc\", 100000, 20, 100000, 20},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tparameter := domain.Parameter{\n\t\t\t\tFunds: tt.funds,\n\t\t\t\tBtc:   tt.btc,\n\t\t\t}\n\t\t\tif parameter.Funds != tt.expectedfunds {\n\t\t\t\tt.Errorf(\"got %q, want %q\", parameter.Funds, tt.expectedfunds)\n\t\t\t}\n\t\t\tif parameter.Btc != tt.expectedbtc {\n\t\t\t\tt.Errorf(\"got %q, want %q\", parameter.Btc, tt.expectedbtc)\n\t\t\t}\n\t\t})\n\t}\n}\n\n```\n\n### Usecase\n\nPlease prepare mock on `testdata` package (if needed) and write tests in the same directory as the usecase.\n\n```zsh\n.\n├── internal\n│   └── app\n│       └── application\n│           ├── service\n│           │   └── exchange.go    # Application Service Interface\n│           └── usecase\n│               ├── ticker.go      # Target Usecase\n│               └── ticker_test.go # Test\n└── testdata\n    └── exchange_mock.go           # Mock of Application Service Interface\n```\n\n```go\n// exchange_mock.go\npackage testdata\n\nimport \"github.com/resotto/goilerplate/internal/app/domain/valueobject\"\n\n// MExchange is mock of service.IExchange\ntype MExchange struct{}\n\n// Ticker is mock implementation of service.IExchange.Ticker()\nfunc (e MExchange) Ticker(p valueobject.Pair) valueobject.Ticker {\n\treturn valueobject.Ticker{\n\t\tSell:      \"1000\",\n\t\tBuy:       \"1000\",\n\t\tHigh:      \"2000\",\n\t\tLow:       \"500\",\n\t\tLast:      \"1200\",\n\t\tVol:       \"20\",\n\t\tTimestamp: \"1600769562\",\n\t}\n}\n\n// Ohlc is mock implementation of service.IExchange.Ohlc()\nfunc (e MExchange) Ohlc(p valueobject.Pair, t valueobject.Timeunit) []valueobject.CandleStick {\n\tcs := make([]valueobject.CandleStick, 0)\n\treturn append(cs, valueobject.CandleStick{\n\t\tOpen:      \"1000\",\n\t\tHigh:      \"2000\",\n\t\tLow:       \"500\",\n\t\tClose:     \"1500\",\n\t\tVolume:    \"30\",\n\t\tTimestamp: \"1600769562\",\n\t})\n}\n```\n\n```go\n// ticker_test.go\npackage usecase_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/resotto/goilerplate/internal/app/application/usecase\"\n\t\"github.com/resotto/goilerplate/internal/app/domain/valueobject\"\n\t\"github.com/resotto/goilerplate/testdata\"\n)\n\nfunc TestTicker(t *testing.T) {\n\ttests := []struct {\n\t\tname              string\n\t\tpair              valueobject.Pair\n\t\texpectedsell      string\n\t\texpectedbuy       string\n\t\texpectedhigh      string\n\t\texpectedlow       string\n\t\texpectedlast      string\n\t\texpectedvol       string\n\t\texpectedtimestamp string\n\t}{\n\t\t{\"btcjpy\", valueobject.BtcJpy, \"1000\", \"1000\", \"2000\", \"500\", \"1200\", \"20\", \"1600769562\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tmexchange := testdata.MExchange{} // using Mock\n\t\t\tresult := usecase.Ticker(mexchange, tt.pair)\n\t\t\tif result.Sell != tt.expectedsell {\n\t\t\t\tt.Errorf(\"got %q, want %q\", result.Sell, tt.expectedsell)\n\t\t\t}\n\t\t\tif result.Buy != tt.expectedbuy {\n\t\t\t\tt.Errorf(\"got %q, want %q\", result.Buy, tt.expectedbuy)\n\t\t\t}\n\t\t\tif result.High != tt.expectedhigh {\n\t\t\t\tt.Errorf(\"got %q, want %q\", result.High, tt.expectedhigh)\n\t\t\t}\n\t\t\tif result.Low != tt.expectedlow {\n\t\t\t\tt.Errorf(\"got %q, want %q\", result.Low, tt.expectedlow)\n\t\t\t}\n\t\t\tif result.Last != tt.expectedlast {\n\t\t\t\tt.Errorf(\"got %q, want %q\", result.Last, tt.expectedlast)\n\t\t\t}\n\t\t\tif result.Vol != tt.expectedvol {\n\t\t\t\tt.Errorf(\"got %q, want %q\", result.Vol, tt.expectedvol)\n\t\t\t}\n\t\t\tif result.Timestamp != tt.expectedtimestamp {\n\t\t\t\tt.Errorf(\"got %q, want %q\", result.Timestamp, tt.expectedtimestamp)\n\t\t\t}\n\t\t})\n\t}\n}\n```\n\n## Naming Convention\n\n### Interface\n\n- Add prefix `I` like `IExchange`.\n  - NOTICE: If you can distinguish interface from implementation, any naming convention will be acceptable.\n\n### Mock\n\n- Add prefix `M` like `MExchange`.\n  - NOTICE: If you can distinguish mock from production, any naming convention will be acceptable.\n\n### File\n\n- File names can be duplicated.\n- For test, add suffix `_test` like `parameter_test.go`.\n- For mock, add suffix `_mock` like `exchange_mock.go`.\n\n### Package\n\n- For package name, please check following posts:\n\n  - [Package names](https://blog.golang.org/package-names)\n  - [Names](https://golang.org/doc/effective_go.html#names)\n\n- For package layout, please check:\n  - [Project Layout](https://github.com/golang-standards/project-layout)\n\n## With Gochk\n\n[Gochk, static dependency analysis tool for go files,](https://github.com/resotto/gochk) empowers Goilerplate so much!\n\n**[Gochk](https://github.com/resotto/gochk) confirms that codebase follows [Clean Architecture The Dependency Rule](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html#the-dependency-rule).**\n\nLet's merge Gochk into CI process.\n\n```yml\nname: test\n\non:\n  push:\n    branches:\n      - master\n    paths-ignore:\n      - \"**/*.md\"\n  pull_request:\n    branches:\n      - master\n\njobs:\n  gochk-goilerplate:\n    runs-on: ubuntu-latest\n    container:\n      image: docker://ghcr.io/resotto/gochk:latest\n    steps:\n      - name: Clone Goilerplate\n        uses: actions/checkout@v2\n        with:\n          repository: {{ github.repository }}\n      - name: Run Gochk\n        run: |\n          /go/bin/gochk -c=/go/src/github.com/resotto/gochk/configs/config.json\n```\n\nAnd then, [its result is](https://github.com/resotto/goilerplate/runs/1367461573):\n\n![Gochk Result in GitHub Actions](https://user-images.githubusercontent.com/19743841/98438959-6f56b680-2131-11eb-8b6e-d835e56239e0.png)\n\n## With PostgreSQL\n\nFirst, you pull the docker image `ghcr.io/resotto/goilerplate-pg` from GitHub Container Registry and run container with following command:\n\n```zsh\ndocker run -d -it --name pg -p 5432:5432 -e POSTGRES_PASSWORD=postgres ghcr.io/resotto/goilerplate-pg:latest\n```\n\nThen, let's check it out:\n\n```zsh\nopen http://0.0.0.0:8080/parameter\nopen http://0.0.0.0:8080/order\n```\n\n### Building Image\n\nIf you fail pulling image from GitHub Container Registry, you also can build Docker image from Dockerfile.\n\n```zsh\ncd build\ndocker build -t goilerplate-pg:latest .\ndocker run -d -it --name pg -p 5432:5432 -e POSTGRES_PASSWORD=postgres goilerplate-pg:latest\n```\n\n### Docker Image\n\nThe image you pulled from GitHub Container Registry is built from the simple Dockerfile and init.sql.\n\n```Dockerfile\nFROM postgres\n\nEXPOSE 5432\n\nCOPY ./init.sql /docker-entrypoint-initdb.d/\n```\n\n```sql\ncreate table parameters (\n    id integer primary key,\n    funds integer,\n    btc integer\n);\n\ninsert into parameters values (1, 10000, 10);\n\ncreate table persons (\n    person_id uuid primary key,\n    name text not null,\n    weight integer\n);\n\ncreate table card_brands (\n    brand text primary key\n);\n\ncreate table cards (\n    card_id uuid primary key,\n    brand text references card_brands(brand) on update cascade\n);\n\ncreate table orders (\n    order_id uuid primary key,\n    person_id uuid references persons(person_id)\n);\n\ncreate table payments (\n    order_id uuid primary key references orders(order_id),\n    card_id uuid references cards(card_id)\n);\n\ninsert into persons values ('f3bf75a9-ea4c-4f57-9161-cfa8f96e2d0b', 'Jerry', 1);\n\ninsert into card_brands values ('VISA'), ('AMEX');\n\ninsert into cards values ('3224ebc0-0a6e-4e22-9ce8-c6564a1bb6a1', 'VISA');\n\ninsert into orders values ('722b694c-984c-4208-bddd-796553cf83e1', 'f3bf75a9-ea4c-4f57-9161-cfa8f96e2d0b');\n\ninsert into payments values ('722b694c-984c-4208-bddd-796553cf83e1', '3224ebc0-0a6e-4e22-9ce8-c6564a1bb6a1');\n```\n\n## Feedbacks\n\n[Feel free to write your thoughts](https://github.com/resotto/goilerplate/issues/1)\n\n## License\n\n[GNU General Public License v3.0](https://github.com/resotto/goilerplate/blob/master/LICENSE).\n\n## Author\n\nResotto\n\n\u003ca href=\"https://github.com/resotto\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/19743841/97778118-4629a980-1bb8-11eb-97ed-76dcdbe50406.png\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://twitter.com/resotto3\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/19743841/97777698-52f8ce00-1bb5-11eb-93c9-b06e0c48b693.png\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/sponsors/resotto\"\u003e\u003cimg src=\"https://img.shields.io/badge/Sponsor-ffffff?logo=github\u0026logoColor=pink\u0026style=flat-square\" /\u003e\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresotto%2Fgoilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fresotto%2Fgoilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresotto%2Fgoilerplate/lists"}