373 lines
12 KiB
Markdown
373 lines
12 KiB
Markdown
<h1 align="center">
|
|
<img height="80" width="160" src="./assets/gopher-icon.gif" alt="Go"><br>Backend Template
|
|
</h1>
|
|
|
|
> Clean architecture based backend template in Go.
|
|
|
|
## Makefile
|
|
|
|
Makefile requires installed dependecies:
|
|
* [go](https://go.dev/doc/install)
|
|
* [docker-compose](https://docs.docker.com/compose/reference)
|
|
* [migrate](https://github.com/golang-migrate/migrate)
|
|
|
|
|
|
```shell
|
|
$ make
|
|
|
|
Usage: make [command] [ENV=staging|production]
|
|
|
|
Commands:
|
|
run Run server (default: staging)
|
|
run ENV=production Run server with production config
|
|
|
|
rename-project name={name} Rename project
|
|
|
|
build-http Build http server
|
|
|
|
migration-create name={name} Create migration
|
|
migration-up Up migrations
|
|
migration-up ENV=production Up migrations (production DB)
|
|
migration-down Down last migration
|
|
|
|
docker-up Up docker services
|
|
docker-down Down docker services
|
|
|
|
fmt Format source code
|
|
test Run unit tests
|
|
|
|
```
|
|
|
|
## HTTP Server
|
|
|
|
The server takes no CLI flags. It reads `ENV_MODE` and loads the matching YAML file from
|
|
[infra/](infra/) — see [Running the Application](#running-the-application) for details.
|
|
|
|
```shell
|
|
# Build, then start with the staging config (default)
|
|
$ go build -o ./bin/http-server ./cmd/server/main.go
|
|
$ ENV_MODE=staging ./bin/http-server
|
|
|
|
# Start with the production config
|
|
$ ENV_MODE=production ./bin/http-server
|
|
```
|
|
|
|
## API Docs
|
|
* [apskel-pos Backend](https://apskel-pos-be.app-dev.altru.id/docs/index.html#/)
|
|
|
|
## License
|
|
|
|
This project is licensed under the [MIT License](https://github.com/pvarentsov/apskel-pos-be/blob/main/LICENSE).
|
|
|
|
# Apskel POS Backend
|
|
|
|
A SaaS Point of Sale (POS) Restaurant System backend built with clean architecture principles in Go.
|
|
|
|
## Architecture Overview
|
|
|
|
This application follows a clean architecture pattern with clear separation of concerns:
|
|
|
|
```
|
|
Handler → Service → Processor → Repository
|
|
```
|
|
|
|
### Layers
|
|
|
|
1. **Contract Package** (`internal/contract/`)
|
|
- Request/Response DTOs for API communication
|
|
- Contains JSON tags for serialization
|
|
- Input validation tags
|
|
|
|
2. **Handler Layer** (`internal/handler/`)
|
|
- HTTP request/response handling
|
|
- Request validation using go-playground/validator
|
|
- Route definitions and middleware
|
|
- Transforms contracts to/from services
|
|
|
|
3. **Service Layer** (`internal/service/`)
|
|
- Business logic orchestration
|
|
- Calls processors and transformers
|
|
- Coordinates between different business operations
|
|
|
|
4. **Processor Layer** (`internal/processor/`)
|
|
- Complex business operations
|
|
- Cross-repository transactions
|
|
- Business rule enforcement
|
|
- Handles operations like order creation with inventory updates
|
|
|
|
5. **Repository Layer** (`internal/repository/`)
|
|
- Data access layer
|
|
- Individual repository per entity
|
|
- Database-specific operations
|
|
- Uses entities for database models
|
|
|
|
6. **Supporting Packages**:
|
|
- **Models** (`internal/models/`) - Pure business logic models (no database dependencies)
|
|
- **Entities** (`internal/entities/`) - Database models with GORM tags
|
|
- **Constants** (`internal/constants/`) - Type-safe enums and business constants
|
|
- **Transformer** (`internal/transformer/`) - Contract ↔ Model conversions
|
|
- **Mappers** (`internal/mappers/`) - Model ↔ Entity conversions
|
|
|
|
## Key Features
|
|
|
|
- **Clean Architecture**: Strict separation between business logic and infrastructure
|
|
- **Type Safety**: Constants package with validation helpers
|
|
- **Validation**: Comprehensive request validation using go-playground/validator
|
|
- **Error Handling**: Structured error responses with proper HTTP status codes
|
|
- **Database Independence**: Business logic never depends on database implementation
|
|
- **Testability**: Each layer can be tested independently
|
|
|
|
## API Endpoints
|
|
|
|
### Health Check
|
|
- `GET /health` - Health check endpoint (registered at the root, not under `/api/v1`)
|
|
|
|
### Organizations
|
|
- `POST /api/v1/organizations` - Create organization
|
|
- `GET /api/v1/organizations` - List organizations
|
|
- `GET /api/v1/organizations/{id}` - Get organization by ID
|
|
- `PUT /api/v1/organizations/{id}` - Update organization
|
|
- `DELETE /api/v1/organizations/{id}` - Delete organization
|
|
|
|
### Users
|
|
- `POST /api/v1/users` - Create user
|
|
- `GET /api/v1/users` - List users
|
|
- `GET /api/v1/users/{id}` - Get user by ID
|
|
- `PUT /api/v1/users/{id}` - Update user
|
|
- `DELETE /api/v1/users/{id}` - Delete user
|
|
- `PUT /api/v1/users/{id}/password` - Change password
|
|
- `PUT /api/v1/users/{id}/activate` - Activate user
|
|
- `PUT /api/v1/users/{id}/deactivate` - Deactivate user
|
|
|
|
### Orders
|
|
- `POST /api/v1/orders` - Create order with items
|
|
- `GET /api/v1/orders` - List orders
|
|
- `GET /api/v1/orders/{id}` - Get order by ID
|
|
- `GET /api/v1/orders/{id}?include_items=true` - Get order with items
|
|
- `PUT /api/v1/orders/{id}` - Update order
|
|
- `PUT /api/v1/orders/{id}/cancel` - Cancel order
|
|
- `PUT /api/v1/orders/{id}/complete` - Complete order
|
|
- `POST /api/v1/orders/{id}/items` - Add item to order
|
|
|
|
### Order Items
|
|
- `PUT /api/v1/order-items/{id}` - Update order item
|
|
- `DELETE /api/v1/order-items/{id}` - Remove order item
|
|
|
|
## Running the Application
|
|
|
|
### Prerequisites
|
|
|
|
| Tool | Version | Needed for |
|
|
|------|---------|------------|
|
|
| [Go](https://go.dev/doc/install) | 1.24+ | building & running the server |
|
|
| [golang-migrate](https://github.com/golang-migrate/migrate) | latest | `make migration-*` targets |
|
|
| [make](https://www.gnu.org/software/make/) | any | shortcut commands (Windows: use Git Bash / WSL, see note below) |
|
|
| [docker & docker-compose](https://docs.docker.com/compose/) | optional | running Postgres/Redis locally |
|
|
| [air](https://github.com/air-verse/air) | optional | hot reload during development (`.air.toml` is already configured) |
|
|
|
|
### 1. Clone & install dependencies
|
|
|
|
```bash
|
|
git clone <repository-url>
|
|
cd apskel-pos-backend
|
|
go mod download
|
|
```
|
|
|
|
### 2. Configuration
|
|
|
|
Configuration is **not** read from `.env` files — it is loaded from YAML files in [infra/](infra/)
|
|
by [config/configs.go](config/configs.go) using viper.
|
|
|
|
The file is selected by the `ENV_MODE` environment variable:
|
|
|
|
| `ENV_MODE` | Config file loaded |
|
|
|------------|--------------------|
|
|
| `local` | `infra/local.yaml` |
|
|
| `development` | `infra/development.yaml` |
|
|
| `staging` *(default)* | `infra/staging.yaml` |
|
|
| `production` | `infra/production.yaml` |
|
|
|
|
Any other/unset value falls back to `staging`. Only `staging.yaml` and `production.yaml` are
|
|
committed — for `local`/`development` copy one of them first:
|
|
|
|
```bash
|
|
cp infra/staging.yaml infra/local.yaml
|
|
```
|
|
|
|
Two important notes:
|
|
|
|
* The config path is **relative to the working directory**, so always run the server from the
|
|
repository root, otherwise viper panics with `failed to read config file`.
|
|
* Push notifications need `infra/firebase-service-account.json` (git-ignored). Without it, obtain
|
|
the file from the team before enabling FCM features.
|
|
|
|
### 3. Run migrations
|
|
|
|
The migration targets build the DB URL from the credentials at the top of the [Makefile](Makefile):
|
|
|
|
```bash
|
|
make migration-up # staging DB (default)
|
|
make migration-up ENV=production # production DB
|
|
|
|
make migration-create name=create_cash_advances_table
|
|
make migration-down # roll back the last migration
|
|
make migration-force version=87 # clear a dirty migration state
|
|
```
|
|
|
|
### 4. Start the server
|
|
|
|
```bash
|
|
make run # ENV_MODE=staging
|
|
make run ENV=production # ENV_MODE=production
|
|
```
|
|
|
|
`make run` is just a wrapper around:
|
|
|
|
```bash
|
|
ENV_MODE=staging go run cmd/server/main.go
|
|
```
|
|
|
|
The server listens on the `server.port` value from the loaded YAML (**4000** for both staging and
|
|
production). Verify it is up:
|
|
|
|
```bash
|
|
curl http://localhost:4000/health
|
|
```
|
|
|
|
All application routes live under `/api/v1` (see [internal/router/router.go](internal/router/router.go)).
|
|
|
|
#### Windows note
|
|
|
|
The `run`/`start` targets use POSIX inline env-var syntax, which `cmd.exe` and PowerShell do not
|
|
understand. Either run `make` from Git Bash / WSL, or start the server directly:
|
|
|
|
```powershell
|
|
# PowerShell
|
|
$env:ENV_MODE = "staging"; go run cmd/server/main.go
|
|
```
|
|
|
|
```cmd
|
|
:: cmd.exe
|
|
set ENV_MODE=staging && go run cmd/server/main.go
|
|
```
|
|
|
|
#### Hot reload
|
|
|
|
```bash
|
|
ENV_MODE=local air # rebuilds ./tmp/main on every .go change
|
|
```
|
|
|
|
### 5. Other commands
|
|
|
|
```bash
|
|
make # show all available targets
|
|
make fmt # go fmt ./...
|
|
make test # go test ./... -v
|
|
|
|
# Build a binary (make build-http still points at the old ./cmd/http path)
|
|
go build -o ./bin/http-server ./cmd/server/main.go
|
|
```
|
|
|
|
### Running with Docker
|
|
|
|
`docker-compose.yaml` provides Postgres (`5432`), Redis (`6379`), and the API image. See
|
|
[DOCKER.md](DOCKER.md) for the full workflow.
|
|
|
|
```bash
|
|
make docker-up # docker-compose up -d
|
|
make docker-down # docker-compose down
|
|
```
|
|
|
|
If you use the containerised Postgres/Redis, point `infra/local.yaml` at `localhost:5432` /
|
|
`localhost:6379` instead of the remote hosts baked into `staging.yaml`.
|
|
|
|
## Example API Usage
|
|
|
|
### Create Organization
|
|
```bash
|
|
curl -X POST http://localhost:4000/api/v1/organizations \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "My Restaurant",
|
|
"plan_type": "premium"
|
|
}'
|
|
```
|
|
|
|
### Create User
|
|
```bash
|
|
curl -X POST http://localhost:4000/api/v1/users \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"organization_id": "uuid-here",
|
|
"username": "john_doe",
|
|
"email": "john@example.com",
|
|
"password": "password123",
|
|
"full_name": "John Doe",
|
|
"role": "manager"
|
|
}'
|
|
```
|
|
|
|
### Create Order with Items
|
|
```bash
|
|
curl -X POST http://localhost:4000/api/v1/orders \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"outlet_id": "uuid-here",
|
|
"user_id": "uuid-here",
|
|
"table_number": "A1",
|
|
"order_type": "dine_in",
|
|
"notes": "No onions",
|
|
"order_items": [
|
|
{
|
|
"product_id": "uuid-here",
|
|
"quantity": 2,
|
|
"unit_price": 15.99
|
|
}
|
|
]
|
|
}'
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
apskel-pos-backend/
|
|
├── cmd/
|
|
│ └── server/ # Application entry point
|
|
├── internal/
|
|
│ ├── app/ # Application wiring and dependency injection
|
|
│ ├── contract/ # API contracts (request/response DTOs)
|
|
│ ├── handler/ # HTTP handlers and routes
|
|
│ ├── service/ # Business logic orchestration
|
|
│ ├── processor/ # Complex business operations
|
|
│ ├── repository/ # Data access layer
|
|
│ ├── models/ # Pure business models
|
|
│ ├── entities/ # Database entities (GORM models)
|
|
│ ├── constants/ # Business constants and enums
|
|
│ ├── transformer/ # Contract ↔ Model transformations
|
|
│ └── mappers/ # Model ↔ Entity transformations
|
|
├── migrations/ # Database migrations
|
|
├── Makefile # Build and development commands
|
|
├── go.mod # Go module definition
|
|
└── README.md # This file
|
|
```
|
|
|
|
## Dependencies
|
|
|
|
- **[Gorilla Mux](https://github.com/gorilla/mux)** - HTTP router and URL matcher
|
|
- **[GORM](https://gorm.io/)** - ORM for database operations
|
|
- **[PostgreSQL Driver](https://github.com/lib/pq)** - PostgreSQL database driver
|
|
- **[Validator](https://github.com/go-playground/validator)** - Struct validation
|
|
- **[UUID](https://github.com/google/uuid)** - UUID generation and parsing
|
|
|
|
## Contributing
|
|
|
|
1. Fork the repository
|
|
2. Create a feature branch
|
|
3. Commit your changes
|
|
4. Push to the branch
|
|
5. Create a Pull Request
|
|
|
|
## License
|
|
|
|
This project is licensed under the MIT License - see the LICENSE file for details.
|