A Go REST API designed to demonstrate a layered, configurable, and decoupled architecture. This service manages company records and user identities, utilizing PostgreSQL for persistence and Kafka for event propagation, docker & docker compose for streamlined deployments, ci workflows & linting for code quality, easy configurability, and type-safe database access with sqlc and pgx.
The application is structured to decouple the transport layer, business logic, and data access layers, enabling strict separation of concerns.
- Business Logic Layers:
- Services Layer (
internal/service): Defines its own interface(Ports) that other layers need to implement to interact with it. Implements business logic and orchestrates data flow. - Domain Layer (
internal/domain): Contains pure entity definitions and domain-specific errors. This layer has no external dependencies.
- Services Layer (
- Data Access Layer:
- Adapters (
internal/repository/adapters): Concrete implementations that satisfy the interfaces set by the service layer. They wrap the repository functions generated bysqlc. - Queries (
internal/queries): Hand-typed SQL queries that get fed to sqlc to generate type-safe go functions that get evaluated at compile-time, foregoing the disadvantages of loosely-typed solutions like ORMs.
- Adapters (
- Transport Layer (
internal/routes/handlers): Handles HTTP requests, validation of the initial request, and response marshaling. It depends only on the Service layer.
-
Secure Authentication:
- User registration with
bcryptpassword hashing. - JWT-based stateless authentication.
- Middleware for protected API routes (
internal/middleware/auth.go).
- User registration with
-
Event Publishing: for all mutating operations via Kafka. Designed again with the port & adapter mentality, implementing the interfaces that the service layer defines. (
internal/events/kafka.go) -
Company Management: Full CRUD capabilities for company records.
-
Observability:
- Structured logging with
uber-go/zap. - Request logging through middleware with comprehensive sanitization for user-controlled elements.
- Structured logging with
-
API Documentation: Integrated Swagger UI serving an OpenAPI specification.
-
Type-Safe SQL: Database interactions generated with
sqlcinstead of an ORM, maintaining explicitness and compile-time query verification. -
Extensive Configuration living in the
.envfile as well as working defaults set inside thecomposefiles and the (internal/config) package of the application.
- Docker & Docker Compose for deployment and demo-ing
- Go for local development
- sqlc & goose for database & query development
The Dockerfile contained in the repository produces a very lean docker image ready for production. It is a two-stage build process where the final Alpine-based docker image essentially only contains the compiled binary.
If you are interested in running the whole project, databases and brokers and all, locally for development or to demo it keep reading.
Clone the repository:
git clone https://github.com/Laelapa/CompanyRegistry.git
cd CompanyRegistrySet up a full deployment, containerized and local with:
docker compose upYou get:
- a postgresql with migrations applied,
- a configured kafka,
- a kafka ui @
localhost:8081, - the Company Registry application,
- with a swagger ui @
localhost:8080/docs,
- with a swagger ui @
everything configured, connected, and ready to go.
To take it down do Ctrl+C
To delete the containers but persist the database & kafka data do:
docker compose downTo completely wipe the volumes too and be ready for a fresh start do:
docker compose down -vTo set up all the peripheral systems (db, kafka, kafka ui) you will run:
docker compose -f compose-peripherals.yaml upThe locally compiled Go program (it's the next step) should connect and communicate with those fine in its default configuration.
To compile the Go program do:
go mod tidy
go build -o company-registry ./cmd/apiMake sure you have the .env file in your project directory, and consider if you want to change anything in the configuration before you compile.
For quick development cycles, to compile & execute without creating a binary file use:
go run ./cmd/apiIf you need to touch SQL queries you will also need sqlc to regenerate the Go wrappers:
Install it with go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
and run it simply with sqlc generate from the project's root folder.
If you intend to touch the database layout too then you will need pressly/goose to manage your migrations.
Get it with go install github.com/pressly/goose/v3/cmd/goose@latest
To run it first pull the .env file into your shell's context with source .env so goose can have access to GOOSE_DBSTRING, or you can set it yourself through the commandline. Read more here: https://pressly.github.io/goose/documentation/cli-commands/
To run the up migrations simply run goose up and you're done.
Run golangci-lint run to execute the linter config defined in the yml
- or
Set up a github workflow(.github/workflows) to have it check whenever you push/pull code into a specific branch.
Thank you very much for showing interest in my project!