Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] [Docs] Add conventions documentation #4718

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions guides/conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Conventions

1. [Application Namespaces](conventions/namespaces.md)
2. [Rails Conventions](conventions/rails.md)
3. [File Structure](conventions/file-structure.md)
10 changes: 10 additions & 0 deletions guides/conventions/file-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Conventions](/guides/conventions.md) /

# File Structure

- [assets](file-structure/assets.md)
- [components](file-structure/components.md)
- [controllers](file-structure/controllers.md)
- [forms](file-structure/forms.md)
- [helpers](file-structure/helpers.md)
- [models](file-structure/models.md)
20 changes: 20 additions & 0 deletions guides/conventions/file-structure/assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[Conventions](/guides/conventions.md) / [File Structure](/guides/conventions/file-structure.md) /

# Assets

Images, videos, and stylesheets are stored in the `assets` directory.

```
.
└ app
└─ assets
├─ images
└─ stylesheets
├─ [namespace]
│ └─ application.scss
└─ application.scss
```

The top-level `application.scss` is the shared stylesheet for all namespaces.

The `find/application.scss` stylesheet should be included into the `layouts/find/application.html.erb` layout alongside the top-level `application.scss` stylesheet.
19 changes: 19 additions & 0 deletions guides/conventions/file-structure/components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[Conventions](/guides/conventions.md) / [File Structure](/guides/conventions/file-structure.md) /

# View Components

We use [View Components](https://viewcomponent.org/) to create reusable components in our Rails applications.

```
.
└ app
└─ components
├─ [namespace] (Namespace-specific components)
│ ├─ [component_name].rb
│ └─ [component_name].html.erb
├─ [model_name] (Model-specific components)
│ ├─ [component_name].rb
│ └─ [component_name].html.erb
├─ [component_name].rb
└─ [component_name].html.erb
```
67 changes: 67 additions & 0 deletions guides/conventions/file-structure/controllers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[Conventions](/guides/conventions.md) / [File Structure](/guides/conventions/file-structure.md) /

# Controllers

We organise our controllers within our namespaces.

We should follow resourceful controller conventions.

```
.
└ app
└─ controllers
├─ [namespace] (Namespace-specific controllers)
│ ├─ [controller_name]_controller.rb
│ └─ application_controller.rb
└─ application_controller.rb
```

Each namespace has its own `ApplicationController` as a base for all controllers within that namespace.

```ruby
# Top-level application controller.
# Shared functionality across all namespaces.
class ApplicationController < ActionController::Base
include Pundit::Authorization
end

# Find application controller.
# Base controller for all Find controllers.
class Find::ApplicationController < ApplicationController
end

# The API application controller inherits from ActionController::API.
# Base controller for all API controllers.
class API::ApplicationController < ActionController::API
end

# The base controller for all v1 API controllers.
class API::V1::ApplicationController < API::ApplicationController
end
```

This allows us to set base functionality across groups of controllers.

The pattern described in [ADR#6 Controller Structure](/guides/adr/0006-controller-structure.md) details a scalable pattern to managing nested resourceful controllers.

```ruby
class Find::RecruitmentCycles::CoursesController < Find::ApplicationController
# GET /recruitment_cycles/:recruitment_cycle_id/courses
def index
@courses = recruitment_cycle.courses
end

# GET /recruitment_cycles/:recruitment_cycle_id/courses/:id
def show
@course = recruitment_cycle.courses.find(params[:id])
end

private

def recruitment_cycle
@recruitment_cycle ||= RecruitmentCycle.find(params[:recruitment_cycle_id])
end
end
```

The `params[:id]` should always be the ID of the resource being acted upon. Parent resources should always be prefixed, e.g. `params[:recruitment_cycle_id]`.
10 changes: 10 additions & 0 deletions guides/conventions/file-structure/file-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Conventions](/guides/conventions.md) /

# File Structure

- [assets](file-structure/assets.md)
- [components](file-structure/components.md)
- [controllers](file-structure/controllers.md)
- [forms](file-structure/forms.md)
- [helpers](file-structure/helpers.md)
- [models](file-structure/models.md)
26 changes: 26 additions & 0 deletions guides/conventions/file-structure/forms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[Conventions](/guides/conventions.md) / [File Structure](/guides/conventions/file-structure.md) /

# Form Objects

We use form objects to encapsulate the validation and parameter handling of forms in our application.

We organise our form objects within our namespaces and shared forms at the top-level.

```
.
└ app
└─ forms
├─ [namespace] (Namespace-specific forms)
│ └─ [form_name]_form.rb
└─ application_form.rb
```

We have a single `ApplicationForm` base Form Object class. This class is used to set the foundations of shared logic across all form objects.

```ruby
# Top-level application form.
# Shared functionality across all form objects.
class ApplicationForm
include ActiveModel::Model
end
```
40 changes: 40 additions & 0 deletions guides/conventions/file-structure/helpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[Conventions](/guides/conventions.md) / [File Structure](/guides/conventions/file-structure.md) /

# View Helpers

View helpers are used to define presentational helpers, especially those that contain HTML. Many times, you'll find yourself needing to manipulate primitive data from models, in this case, put the presentational methods on the models themselves.

```
.
└ app
└─ helpers
├─ [namespace] (Namespace-specific forms)
│ └─ [helper_name]_helper.rb
└─ application_helper.rb
```

There is no inheritance between helpers, so you can define a helper method in any helper file and use it in any view.

A helper file should be a collection of related helper methods. A helper file is structured around either a behavioural context or a resourceful context.

Below is an example of a behavioural helper file:

```ruby
module LinkHelper
def active_link_to(text, href)
link_to text, user_path(user), class: class_names("active", current_page?(href))
end
end
```

Below is an example of a resourceful helper file, these helpers should be nested within the namespace of the resource:

```ruby
module CourseHelper
def course_status_tag(course)
govuk_tag(course.status, color: "green")
end
end
```

All resourceful helpers should be prefixed with the resource name.
31 changes: 31 additions & 0 deletions guides/conventions/file-structure/models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[Conventions](/guides/conventions.md) / [File Structure](/guides/conventions/file-structure.md) /

# Models

We follow standard Rails conventions for models. Models are stored in the `app/models` directory.

The `app/models` directory should contain only ActiveRecord models. Non-ActiveRecord PORO classes should be in the `app/lib` directory.

```
.
└ app
└─ models
├─ [namespace] (Namespace-specific models)
│ └─ [model_name].rb
└─ application_record.rb
```

Models should contain the following logic:

- Associations
- Validations
- Scopes
- Computed getter methods (Presentational methods)

We should refrain from adding business logic to models. Business logic belongs in service objects classes.

We should also refrain from adding callbacks to models. Callbacks are traditionally used to trigger side-effects and that logic belongs in service objects.

Additionally, we should be aware of all mutation points of models. By calculating computed values within service objects, we can test side-effects and coupled attributes in isolation.

We should limit scopes and define complex queries in query objects.
26 changes: 26 additions & 0 deletions guides/conventions/namespaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[Conventions](/guides/conventions.md) /

# Namespaces

We use namespaces to organise our code into service-specific modules. This provides us with visibility of separation of concerns.

We benefit from this separtation in the following ways:

- Code under a namespace is intended to be used only by the service it belongs to.

We can easily detect when code is used incorrectly outside of its namespace.

This limits the blast radius of changes to a single service. For example, we can confidently make changes to a `Find` component and expect that it will only effect the "Find" service.

- Explicit shared code.

If code is shared, it is in the top-level namespace.

We can visually determine how much code is shared between services by looking at the non-namespaced files.

## Our namespaces

- `Find`
- `Publish`
- `Support`
- `API`
66 changes: 66 additions & 0 deletions guides/conventions/rails.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
[Conventions](/guides/conventions.md) /

# Rails Architecture

This document describes the Rails conventions of this application.

## 1. File Structure

Learn more about each of the directories' purposes in the [File Structure](conventions/file-structure.md) guide.

```
.
└ app
├─ assets
├─ components (View Components)
├─ controllers
├─ forms (Form Objects)
├─ helpers
├─ javascript
├─ jobs
├─ lib
├─ mailers
├─ models
├─ policies
├─ serializers
├─ services (Service Objects)
├─ validators
├─ views
└─ wizards
```

The following directories are deprecated and should be removed:

- `app/decorators`

We should put decorator-type methods in models themselves.

- `app/view_objects`

We should use View Components (`app/components`) instead.

## 2. Environments

There are 2 kinds of environments we need to maintain:

1. Rails environment (`RAILS_ENV`)

There are 3 Rails environments:

- `production`
- `test`
- `development`

2. Application environment (`APP_ENV`) - This is commonly known as Hosting environment in other BAT applications.

This environment is used to determine the settings of the application.

There are 5 Application environments:

- `review`
- `qa`
- `staging`
- `sandbox`
- `production`

The `loadtest` and `rollover` environments are deprecated and should be removed. We can use `staging` temporarily for these use-cases.
Loading