This is a service that helps managing Workouts
- jackc/pgx as PostgreSQL driver
- pressly/goose as Database Migration Tool
- go-chi/chi as HTTP routing
- Stateful Token for authentication
curl -X POST "http://localhost:8080/users" \
-H "Content-Type: application/json" \
-d '{
"username": "melkey",
"email": "melkey@example.com",
"password": "SecureP@ssword123",
"bio": "Fitness enthusiast and software developer"
}'curl -X POST "http://localhost:8080/tokens/authentication" \
-H "Content-Type: application/json" \
-d '{
"username": "johndoe",
"password": "SecureP@ssword123"
}'curl -X POST "http://localhost:8080/workouts" \
-H "Authorization: Bearer 3VGSV7LBT4IVASURIKUOUWOF5WGHOWRXUZA6OLKK7U4ABNCLPVSA" \
-H "Content-Type: application/json" \
-d '{
"title": "Morning Cardio",
"description": "A light 30-minute jog to start the day.",
"duration_minutes": 30,
"calories_burned": 300,
"entries": [
{
"exercise_name": "Jogging",
"sets": 1,
"duration_seconds": 1800,
"weight": 0,
"notes": "Maintain a steady pace",
"order_index": 1
}
]
}'curl -X PUT "http://localhost:8080/workouts/6" \
-H "Authorization: Bearer M6BOKTWSQJL74GJJO5OIOMOG3V63MYRIKLUBZ6ILEUQUPCN7472Q" \
-H "Content-Type: application/json" \
-d '{
"title": "Updated Cardio",
"description": "A relaxed 45-minute walk after dinner.",
"duration_minutes": 45,
"calories_burned": 250,
"entries": [
{
"exercise_name": "Walking",
"sets": 1,
"duration_seconds": 2700,
"weight": 0,
"notes": "Keep a steady pace",
"order_index": 1
}
]
}'This project follows several well-established architectural principles and patterns for building a robust and maintainable Go web service.
The project is organized into distinct layers with clear separation of concerns:
- Presentation Layer -
internal/apihandlers - Business/Application Layer -
internal/appapplication setup - Data Access Layer -
internal/storedata persistence - Infrastructure Layer -
internal/middleware,internal/routes
- The
app.NewApplication()function creates and wires dependencies - Handlers receive store interfaces through constructors (e.g.,
NewWorkoutHandler)
- Abstract interfaces like
WorkoutStore,UserStore, andTokenStore - Concrete implementations like
PostgresWorkoutStore
- Small, focused interfaces for each store type
- Handlers depend on interfaces, not concrete implementations
- Each handler has a single responsibility (e.g.,
WorkoutHandleronly handles workout operations) - Separate concerns: authentication (
middleware), routing (routes), data access (store)
- Authentication and authorization implemented as middleware in
internal/middleware/middleware.go - Applied via
routes.SetupRoutes()
- Domain entities like
Workout,User - Business logic encapsulated in methods (e.g., password hashing in
user_store.go)
- Structured migrations in
migrationsdirectory - Embedded filesystem pattern with
migrations/fs.go
- Version field in workouts for handling concurrent updates
- Implemented in
UpdateWorkoutmethod - Test scripts demonstrate this pattern:
test_scripts/test_optimistic_concurrency_control.go
- Standard HTTP methods and status codes
- Resource-based URLs (e.g.,
/workouts/{id}) - JSON request/response format using
utils.WriteJSON
This architecture promotes:
- Testability - Clear interfaces make unit testing easier
- Maintainability - Separation of concerns allows for easier modifications
- Loose Coupling - Components depend on abstractions, not concrete implementations
- Scalability - Layered approach allows for easy horizontal scaling
- Code Reusability - Interface-based design promotes code reuse