- Database: PostgreSQL
- Reasoning: "Upskill" requires unique relationships with rigid data fields (i.e. email, courses, id) corresponing to an individual user. This data must be quickly accesible as numerous values may be requested from multiple users at the same time. A relational database works best in this scenario. Postgres is also great for data integrity, a must for a product that demands mutliple entities (i.e. students, teachers, courses) are receiving the appropriate work.
- Database Driver:
pgx- pgx is a pure Go driver and toolkit for PostgreSQL designed for high performance and access to PostgreSQL-specific features. It is widely considered the standard for Go/Postgres applications.
- Used Handler-Service-Repository pattern
- Advantages: decoupling, testing, flexibility.
- Ensures each layer only executes functions in accordance to its purpose.
- Router:
chi- An idiomatic router for building HTTP services in Go,
chiis the ideal choice.
- An idiomatic router for building HTTP services in Go,
- Security
- Used bcrypt.DefaultCost to ensure password integrity; the cost factor and salting increases the time required to hash each password, but makes brute-force attacks higher.
- Logging
- Returned sentinel erorr,
ErrInvalidCredentials, for both no-user and bad-password to prevent enumeration.
- Returned sentinel erorr,
- Routes
- Chose
/loginas opposed to more RESTful/sessionsas endpoint for user login./sessionsreflects a strict REST/resource-oriented worldview ("a session is a thing you create"), while /login reflects an action-oriented worldview ("login is something you do"), which feels more in accordance with this product's current mental framework.
- Chose
- New migration with three real schema changes (renames, NOT NULL, FK with ON DELETE behavior) plus a clean down-migration that round-trips.
- Full courses domain across all three architectural layers.
- A new cross-domain dependency (courses -> users service) wired correctly through dependency injection.
- Real business logic in the service (teacher-role check) with proper sentinel errors.
- HTTP status code mapping that actually distinguishes 403 vs 404 vs 500.
- Backfilled GetByID on the users service so future domains can reuse it.
-
Preserving API architectue
- Courses full mirrors the handler -> service -> repository setup from users.
-
Enforcing Teacher-Owner constraint on courses table
- There's two options here: enforce at the application layer or at the database level. I chose to enforce this check at the service layer because if an attempt to make a non-teacher an owner of a course is made, it can be easily flagged. Also, fixing this problem is easier here than at the DB-level. If in the future, I add a role, it would involve appling a new constraint to the table and that won't as easy.
-
Zero Parameters for
courses.repository.List()List()inrepository.gocurrently doens't have any parameters that are passed to it's db query. There currently isn't a need for to pass arguments, but if this app were to someday have thousands of courses, it may be helpful to introduce pagination so we don't return so many rows at once.