๐งบ Store Golang Microservices Sample
A practical store sample, built with Golang and different software architecture and technologies like Microservices Architecture, Vertical Slice Architecture , CQRS Pattern, Domain Driven Design (DDD), Event Sourcing, Event Driven Architecture. For communication between independent services, We use asynchronous messaging with using RabbitMQ, and sometimes we use synchronous communication for real-time communications with using REST and gRPC calls.
in-progress
and I will add new features and thecnologies over time.
Features
โ UsingVertical Slice Architecture
as a high level architectureโ UsingEvent Driven Architecture
on top of RabbitMQ Message Broker with a custom Event Busโ UsingData Centeric Architecture
based on CRUD in Catalogs Read Serviceโ UsingEvent Sourcing
inAudit Based
services like Orders Serviceโ UsingCQRS Pattern
andMediator Pattern
on top of Go-MediatR libraryโ Using RESTFul api with Echo framework and using swagger with swaggo/swag libraryโ Using gRpc for internal service communicationโ Using go-playground/validator for validating input data in the REST and gRpcโ UsingPostgres
andEventStoreDB
for write databases as relational DB andMongoDB
andElasric Search
for read databasesโ UsingOpenTelemetry
for collectionMetrics
andDistributed Tracing
โ Using docker anddocker-compose
for deployment
Roadmap
๐ง AddDependecny Injections
with using uber-go/fx๐ง UsingDomain Driven Design
in some of services like Catalogs Write Service๐ง UsingHelm
andKubernetes
for deployment๐ง AddIdentity Management
andOAuth
Technologies - Libraries
โ๏ธ labstack/echo
- High performance, minimalist Go web frameworkโ๏ธ uber-go/zap
- Blazing fast, structured, leveled logging in Go.โ๏ธ emperror/errors
- Drop-in replacement for the standard library errors package and github.com/pkg/errorsโ๏ธ open-telemetry/opentelemetry-go
- OpenTelemetry Go API and SDKโ๏ธ open-telemetry/opentelemetry-go-contrib
- Collection of extensions for OpenTelemetry-Go.โ๏ธ rabbitmq/amqp091-go
- An AMQP 0-9-1 Go client maintained by the RabbitMQ team. Originally by @streadway:streadway/amqp
โ๏ธ stretchr/testify
- A toolkit with common assertions and mocks that plays nicely with the standard libraryโ๏ธ mehdihadeli/go-mediatr
- Mediator pattern implementation in Golang and helpful in creating CQRS based applications.โ๏ธ grpc-ecosystem/go-grpc-middleware
- Golang gRPC Middlewares: interceptor chaining, auth, logging, retries and moreโ๏ธ grpc/grpc-go
- The Go language implementation of gRPC. HTTP/2 based RPCโ๏ธ elastic/go-elasticsearch
- The official Go client for Elasticsearchโ๏ธ avast/retry-go
- Simple golang library for retry mechanismโ๏ธ ahmetb/go-linq
- .NET LINQ capabilities in Goโ๏ธ EventStore/EventStore-Client-Go
- Go Client for Event Store version 20 and above.โ๏ธ olivere/elastic/v7
- Deprecated: Use the official Elasticsearch client for Go atโ๏ธ swaggo/swag
- Automatically generate RESTful API documentation with Swagger 2.0 for Go.โ๏ธ prometheus/client_golang
- Prometheus instrumentation library for Go applicationsโ๏ธ mongodb/mongo-go-driver
- The Go driver for MongoDBโ๏ธ go-redis/redis
- Type-safe Redis client for Golangโ๏ธ go-gorm/gorm
- The fantastic ORM library for Golang, aims to be developer friendlyโ๏ธ go-playground/validator
- Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
System Architecture
Application Structure
In this project I used vertical slice architecture or Restructuring to a Vertical Slice Architecture also I used feature folder structure in this project.
- We treat each request as a distinct use case or slice, encapsulating and grouping all concerns from front-end to back.
- When We adding or changing a feature in an application in n-tire architecture, we are typically touching many different "layers" in an application. we are changing the user interface, adding fields to models, modifying validation, and so on. Instead of coupling across a layer, we couple vertically along a slice and each change affects only one slice.
- We
Minimize coupling
between slices
, andmaximize coupling
in a slice
. - With this approach, each of our vertical slices can decide for itself how to best fulfill the request. New features only add code, we're not changing shared code and worrying about side effects. For implementing vertical slice architecture using cqrs pattern is a good match.
Also here I used CQRS for decompose my features to very small parts that makes our application:
- maximize performance, scalability and simplicity.
- adding new feature to this mechanism is very easy without any breaking change in other part of our codes. New features only add code, we're not changing shared code and worrying about side effects.
- easy to maintain and any changes only affect on one command or query (or a slice) and avoid any breaking changes on other parts
- it gives us better separation of concerns and cross cutting concern (with help of MediatR behavior pipelines) in our code instead of a big service class for doing a lot of things.
With using CQRS, our code will be more aligned with SOLID principles, especially with:
- Single Responsibility rule - because logic responsible for a given operation is enclosed in its own type.
- Open-Closed rule - because to add new operation you donโt need to edit any of the existing types, instead you need to add a new file with a new type representing that operation.
Here instead of some Technical Splitting for example a folder or layer for our services
, controllers
and data models
which increase dependencies between our technical splitting and also jump between layers or folders, We cut each business functionality into some vertical slices, and inner each of these slices we have Technical Folders Structure specific to that feature (command, handlers, infrastructure, repository, controllers, data models, ...).
Usually, when we work on a given functionality we need some technical things for example:
- API endpoint (Controller)
- Request Input (Dto)
- Request Output (Dto)
- Some class to handle Request, For example Command and Command Handler or Query and Query Handler
- Data Model
Now we could all of these things beside each other and it decrease jumping and dependencies between some layers or folders.
Keeping such a split works great with CQRS. It segregates our operations and slices the application code vertically instead of horizontally. In Our CQRS pattern each command/query handler is a separate slice. This is where you can reduce coupling between layers. Each handler can be a separated code unit, even copy/pasted. Thanks to that, we can tune down the specific method to not follow general conventions (e.g. use custom SQL query or even different storage). In a traditional layered architecture, when we change the core generic mechanism in one layer, it can impact all methods.