Organizing Your Codebase with Feature Verticals Architecture

Feature verticals architecture is a way of organizing your codebase so that it is easy to understand and maintain. It involves grouping together code that performs related tasks or is part of the same feature.

One way to implement feature verticals is through use cases, which are independent pieces of code that exist within one logical flow. These use cases may contain all the functions or services needed for a particular feature, or they may refer to other code.

A common example of a use case is an HTTP route handler, which is responsible for handling incoming HTTP requests and sending back a response.

func handle(r http.Request, w *http.Response) {
    params, err := parseParams(r)
    if err != nil {
        writeErr(w, err)
        return
    }
    
    row := db.Exec(`select id, title from ... where di = ?`, params.P1)
    var data Response{}
    err := row.Scan(&data.ID, &data.Title)
    if err != nil {
        writeErr(w, err)
        return
    }
    
    w.Write(data)
}

Feature verticals architecture can be contrasted with a layered architecture, which involves dividing the codebase into different layers based on their responsibilities. While a layered architecture can provide a clear separation of concerns and make it easier to understand the codebase, it can also add unnecessary complexity and slow down the development of an MVP (Minimum Viable Product).

In a feature verticals architecture, it is important to keep related code together and avoid preemptive optimization. This means avoiding the introduction of unnecessary layers or abstraction complexity before it is actually needed. The SOLID principles, particularly the single responsibility principle, can be helpful in guiding the organization of code in a feature verticals architecture.

It is also important to consider the stage of the project when deciding on an architecture. In the early stages of a project, it may be more important to focus on individual features rather than infrastructure details. This may mean grouping related code together and avoiding premature optimization.

Ultimately, the right architecture will depend on the context of the project and the needs of the team. The goal should be to create an architecture that is easy to understand, flexible, and makes the work of the team easier.