Backend Developer — Building with Go and Backend Architecture
R.
In Progress

Go-mono-project

Go-Mono-Project is a Go REST APIs rewrite of my original Spring Boot–based monitoring system. It retains core features like project tracking, RBAC, and department-level management, while improving performance, structure, and maintainability through a cleaner and more modular Go backend.

GolangPostgresREST APIsRBAC

Overview

What was built

A Go-based REST API rewrite of an original Spring Boot project monitoring system. Handles project tracking, role-based access control (RBAC), and department-level management for internal teams.

Why it was built

The original Spring Boot implementation carried JVM startup overhead and verbose boilerplate that slowed iteration. Rewriting in Go produced a leaner binary, lower memory footprint, and forced a clean architectural rethink: layered handler/service/repository instead of an organically grown monolith.

Tech stack
GolangPostgreSQLpgxJWTDocker
Key features
JWT stateless auth
Per-resource RBAC
Project CRUD with department scope
User management + role assignment
Structured JSON error responses
Handler / Service / Repository layers

Architecture & Design

Single Go binary exposing a JSON REST API. Requests pass through middleware (JWT auth, logging, CORS) before reaching domain handlers. Handlers delegate to services which enforce RBAC and call repositories that execute parameterised SQL via pgx.

Client
HTTP Client

Any REST client (Postman, frontend, or service)

Middleware (HTTP request)
API Layer
Middleware

JWT auth, request logging, CORS

Domain Handlers (authenticated)
Domain Handlers

Route parsing, request binding, response shaping

Service Layer (domain call)
Service Layer
Service Layer

Business logic, RBAC enforcement, validation

Repository Layer (data access)
Data Layer
Repository Layer

Parameterised SQL via pgx, data mapping

PostgreSQL (SQL / pgx)
PostgreSQL

Persistent storage for projects, users, departments

Client
API Layer
Service Layer
Data Layer

Challenges & Solutions

Problem

Internal teams lacked a centralised way to track project progress across departments. The existing Spring Boot codebase had grown without a clean separation of concerns, making it slow to change and expensive to run for the workload it handled.

Constraints

  • API contract must remain backward-compatible with the existing frontend
  • RBAC logic must match the original permission model exactly
  • No external auth service; authentication handled in-process

Approach

Adopted a layered Go architecture (handler, service, repository) with constructor-based dependency injection. Each domain is an isolated package. RBAC is enforced at the service layer, not in middleware, so permission checks stay adjacent to business logic and are testable without HTTP context. PostgreSQL access uses pgx with parameterised queries throughout.

Technical challenges
01
Challenge

Translating Spring Boot's annotation-driven DI to idiomatic Go without a framework.

Solution

Constructor injection throughout: each layer receives dependencies as parameters. A single wiring function in main.go composes the full dependency graph explicitly.

Outcome

No reflection or hidden magic. Fully traceable dependency graph, straightforward to mock in tests.

02
Challenge

Replicating the original per-role, per-resource RBAC permission matrix in Go.

Solution

Defined a Permission type and a role → []Permission map in a dedicated auth package. Service functions check permissions before any mutation executes.

Outcome

RBAC is centrally defined, consistently enforced, and testable in isolation without HTTP context.

03
Challenge

Preventing SQL injection without an ORM to enforce parameterised queries.

Solution

Mandated pgx parameterised queries across all repository functions, with no raw string formatting of user input.

Outcome

Zero SQL injection surface. Type mismatches between Go and Postgres types caught at compile time.

Engineering decisions
01

Go over Java / Spring Boot

Rationale

Self-contained binary, near-instant startup, and lower memory footprint than JVM. Forces explicit dependency wiring with no annotation magic to hide coupling.

Tradeoffs

Loses Spring Security, Hibernate, and the broader JVM ecosystem. Auth and data-access layers must be composed from smaller, explicit libraries.

02

pgx over database/sql + GORM

Rationale

pgx is the highest-performance PostgreSQL driver for Go and exposes Postgres-native types. Explicit SQL stays readable and debuggable without ORM abstraction.

Tradeoffs

More verbose than GORM for simple CRUD. Schema migrations must be managed separately (e.g., golang-migrate).

03

Layered packages over flat structure

Rationale

Enforces that HTTP concerns don't bleed into business logic, and business logic doesn't bleed into SQL. Each layer is testable independently.

Tradeoffs

More files and indirection up front. Justified only once the endpoint count grows beyond trivial size.

Results & Learnings

Outcomes
01

All original monitoring features rebuilt in Go with a clean layered structure

02

Stateless JWT auth and RBAC enforced across all resource types

03

Parameterised SQL throughout with no SQL injection surface

Learnings

Key Lessons

  • Constructor injection in Go is sufficient; no DI framework needed
  • RBAC belongs in the service layer, not middleware, for testability and locality
  • Explicit SQL is more maintainable than ORM magic when the team knows SQL

Future Improvements

  • Add golang-migrate for reproducible schema migrations
  • Integration test suite covering all RBAC permission scenarios
  • OpenAPI spec generated from handler signatures

What I'd Do Differently

  • Define the OpenAPI contract before writing handlers to prevent response shape drift
  • Set up CI on day one; retrofitting it later costs more than the initial setup