Casper DAO Middleware is a mono-repository that consists of two apps. Handler is used to listen to event from the network, process them and store them in DB and API for exposing stored data.
Check various build options with:
make help
- Install go-migrate
- Run local migrations
make sync-db
Each component could be tested via unit or integrations tests.
To run unit tests for the component, make sure you are in the root of the component:
cd internal/dao/{component} && go test ./...
Setup database command:
make sync-test-db
After your environment is ready, make sure you set up required env variables.
Run integration tests:
cd internal/dao/{component} && go test -tags=integration ./...
-
Install docker and docker-compose
-
Run local docker-compose.yaml file
docker-compose -f ./infra/local/docker-compose.db.yaml up -d
- Prepare .env files
cp ./apps/handler/.env.example ./apps/handler/.env && cp ./apps/api/.env.example ./apps/api/.env
- Run local migrations
make sync-db
- Run application
cd ./apps/{app} && go run .
To generate swagger from the code comments swag
should be installed locally:
Installation instruction could be found here
To run swagger regeneration:
make swagger
The following tools should be enabled in your IDE:
goimports
golangci-lint
Check this link
The project should be readable, extendable, and flexible. In order to achieve it will follow the layered architecture approach with the following layers:
┌────────────────────────────────────┐
│ Application/Infrastructure layer │
├────────────────────────────────────┤
│ Service layer │
├────────────────────────────────────┤
│ Domain objects layer │
├────────────────────────────────────┤
│ Data access layer │
└────────────────────────────────────┘
The approach above is a modification of the Fowler's multilayer presentation domain application layering:
with the following differences:
- Presentation layer is renamed to Application/Infrastructure layer to better reflect its nature
- Data mapper layer is absent because data mapping is done by Go's
sqlx
library with struct tags
It is important to follow Fowler's advice and split the project into domain-oriented sub-modules:
It is important to keep a finite number of building blocks used in the codebase on the lower level of abstractions. The most typical examples are:
- entities
- repositories
- services (Ihor: I want to try using commands as service actions because they provide a high level of isolation and are easy to maintain because there is no order for input parameters)
- errors (we should make sure to properly map them in the API, each error should have its own human-readable code, see Stripe API for example)
- validations (should be as granular as possible)
- events (should be used to link pieces of code where the lowest level of coupling is expected)
In order to make project-level communications easier, the codebase should be built with domain-driven design principles in mind.
In order to satisfy the architecture outlined above, the project structure should be the following:
casper-middleware
├───apps The apps directory contains applications
│ └───sample-app An application is a piece of code that has a single function.
│ ├───resources It can have its own resources like configuration, secret keys, etc.
│ │ ├───config.go
│ │ └───etc
│ ├───etc It can contain infrastructure code in subpackages or files.
│ ├───etc.go It can use one or more domain packages defined in the internal directory
│ ├───main.go It always has an entry point
│ └───README.md It always has a README file
│
├───infra The infra directory contains infrastructure-related files
│ ├───docker It can be Docker files
│ ├───terraform or Terraform scripts
│ └───etc
│
├───internal The internal directory contains domain logic packages. It could be called
│ └───sample-domain-package domain or business-logic, but internal may be a better name for a Go project
│ ├───entities A domain package has an entities directory that contains managed domain entities
│ ├───events It can have an events directory for domain-specific events
│ ├───errors It can have an errors directory for domain-specific errors
│ ├───repositories It has n repositories directory with, well, repositories
│ ├───resources It can have a resources directory with database migrations, etc
│ │ └───migrations
│ ├───services It has a services directory with services that describe available
│ │ ├───service-one domain-specific actions. Which can be grouped by a service name or
│ │ ├───service-two can be listed directly in the directory for smaller packages
│ │ ├───...
│ │ └───service-n
│ ├───validations It can have a validations directory with validations that return domain errors
│ ├───etc
│ └───README.md It always has a README file
│
├───pkg The pkg directory contains "third-party" utilities needed by the project
│ └───sample-utility-package Such packages don't expose any domain logic and theoretically can be open-sourced
│ └───README.md
│
└───Makefile Makefile represents singe entry for the project-related commands
Plural names were used for higher-level packages to have a possibility to reuse singular names in the code.
The structure shouldn't be considered final and should evolve together with the project.