Skip to content

Latest commit

 

History

History
1172 lines (889 loc) · 42.3 KB

03_BRANCH_DESCRIPTIONS.md

File metadata and controls

1172 lines (889 loc) · 42.3 KB

MENU README | How to run locally | REST API doc | Web app screenshots | Branch descriptions

🚆 Rails Way App

This document presents the branches of the Rails Way App project.

📚 Table of contents


010-one-controller-per-entity

Lines of Code1326
Rubycritic Score89.23

In this version, a single controller is used for each main entity of the system (model).

This approach is quite common, given that the scaffold generator (presented in almost every Rails tutorial) creates a controller for each model. Based on this premise, it is intuitive to keep a single controller per model and add new actions if necessary.

As a side effect, the user controller contains the largest number of actions (Registration, Authentication, Account Deletion, API Token Refresh, Password Update, and Password Reset). Since it handles different operations, it is less cohesive than the others.

110 app/controllers/application_controller.rb
130 app/controllers/task_items_controller.rb
82  app/controllers/task_lists_controller.rb
206 app/controllers/users_controller.rb
528 total

🤔 What is the problem with low cohesion code?

Low cohesion leads to greater coupling, higher costs, and efforts to promote changes.

🔎 What the next version will have?

It will address the low cohesion of the user controller by extracting concerns into separate modules. This way, each concern will be responsible for specific actions, making the code more cohesive.

Next version: 011-one-controller-per-entity_user-concerns

⬆ back to top


011-one-controller-per-entity_user-concerns

Lines of Code1350
Rubycritic Score90.34

Refactoring with ActiveSupport::Concern:

This is how this feature is presented in the Rails Guides:

Concerns are a way to make large controllers or models easier to understand and manage. This also has the advantage of reusability when multiple models (or controllers) share the same concerns.

Since the user controller has the largest number of actions, this version makes use of ActiveSupport::Concern to separate the different responsibilities of this controller. Here's how the distribution between the files looks:

 21 app/controllers/users_controller.rb
 48 app/controllers/concerns/user_passwords_concern.rb
 41 app/controllers/concerns/user_profiles_concern.rb
 48 app/controllers/concerns/user_registrations_concern.rb
 49 app/controllers/concerns/user_sessions_concern.rb
 38 app/controllers/concerns/user_tokens_concern.rb
245 total

We can see a positive impact on the Rubycritic score, which went from 89.23 to 90.24.

However, it is important to note that a concern is a mixin. That is, methods with the same name will be overridden. That is why each concern file needs to maintain the prefixes or suffixes in its methods (Examples: new_session, create_session, user_session_params...).

In this case, the use of mixins is just separating a large class into several smaller ones, but in the end, we end up having the same large class, but with its implementation in separate files.

🤔 Would it be possible to achieve this separation and avoid this collision?

The answer is yes! 🙌

🔎 What the next version will have?

It shows how to separate the concerns into different controllers. This way, we can have a better separation of responsibilities and avoid the collision of methods with the same name.

Next version: 020-multi-controllers-per-entity.

⬆ back to top


020-multi-controllers-per-entity

Lines of Code1342
Rubycritic Score91.34

The previous version demonstrates how concerns can help safely move code around, facilitating a better understanding of the different responsibilities in an implementation.

These were the created concerns:

  • UserRegistrationsConcern
  • UserSessionsConcern
  • UserPasswordsConcern
  • UserTokensConcern
  • UserProfilesConcern

However, since the concerns are mixins, we need to ensure that all method names are unique. After all, if any are repeated, they will overwrite each other.

And here is what this version does. It uses the concerns categorization to implement dedicated routes and controllers.

See how the controllers turned out:

110 app/controllers/application_controller.rb
130 app/controllers/task_items_controller.rb
 82 app/controllers/task_lists_controller.rb
 60 app/controllers/user_passwords_controller.rb
 41 app/controllers/user_profiles_controller.rb
 49 app/controllers/user_registrations_controller.rb
 50 app/controllers/user_sessions_controller.rb
 25 app/controllers/user_tokens_controller.rb
547 total

🤔 What was changed?

The Rubycritic score increased from 90.34 to 91.34.

This happened because each controller allowed the isolation of each action and callback and allowed the definition of methods with the same name. (Example: user_params instead of user_registration_params, user_session_params, user_password_params...).

Another benefit was the routing definition. It became more readable and easier to understand as it was possible to declare them using only default REST actions (index, show, new, create, edit, update, destroy).

🔎 What the next version will have?

It shows how the restriction of REST actions can enforce cohesion and ensure controllers are responsible for specific contexts/concepts.

Next version: 021-multi-controllers-per-entity_rest-actions-only.

⬆ back to top


021-multi-controllers-per-entity_rest-actions-only

Lines of Code1361
Rubycritic Score91.56

This version ensures that all controllers only have REST actions.

To accomplish this the task_items#complete and task_items#incomplete actions were moved to their own controller:

From To
TaskItemsController#complete CompleteTaskItemsController#update
TaskItemsController#incomplete IncompleteTaskItemsController#update

Beyond this change, concern was created to share code between the CompleteTaskItemsController, IncompleteTaskItemsController, and TaskItemsController.

See how the task items controllers are now:

app/controllers/
├──  concerns/
│  └── task_items_concern.rb
├── complete_task_items_controller.rb
├── incomplete_task_items_controller.rb
└── task_items_controller.rb

🤔 What was changed?

The Rubycritic score increased from 91.34 to 91.56.

This happened because each cohesion has been increased, and the controllers are more specialized.

But lets be honest, the routes are worse than before. 😅

🔎 What the next version will have?

Let's do what DHH taught us over a decade ago: https://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/

This will improve the routes and put the controllers in a better structure.

Next version: 030-resources-within-namespaces.

⬆ back to top


030-resources-within-namespaces

Lines of Code1361
Rubycritic Score91.56

This version implements the ideas presented in this article https://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/, and introduces the concept of namespaces to controllers and routes.

Controllers:

Before After
app/controllers
├── concerns
│  └── task_items_concern.rb
├── application_controller.rb
├── complete_task_items_controller.rb
├── incomplete_task_items_controller.rb
├── task_items_controller.rb
├── task_lists_controller.rb
├── user_passwords_controller.rb
├── user_profiles_controller.rb
├── user_registrations_controller.rb
├── user_sessions_controller.rb
└── user_tokens_controller.rb
app/controllers
├── concerns
│  └── task_items_concern.rb
├── application_controller.rb
├── task
│  ├── items
│  │  ├── complete_controller.rb
│  │  └── incomplete_controller.rb
│  ├── items_controller.rb
│  └── lists_controller.rb
└── user
   ├── passwords_controller.rb
   ├── profiles_controller.rb
   ├── registrations_controller.rb
   ├── sessions_controller.rb
   └── tokens_controller.rb

Routes

Before After
/user_sessions
/user_sessions/new
/user_registrations
/user_registrations/new
/user_passwords
/user_passwords/new
/user_passwords/:id/edit
/user_passwords/:id
/user_profiles/edit
/user_profiles
/user_tokens/edit
/user_tokens
/task_lists/:task_list_id/task_items
/task_lists/:task_list_id/task_items/new
/task_lists/:task_list_id/task_items/:id/edit
/task_lists/:task_list_id/task_items/:id
/task_lists/:task_list_id/complete_task_items/:id
/task_lists/:task_list_id/incomplete_task_items/:id
/task_lists
/task_lists/new
/task_lists/:id/edit
/task_lists/:id
/user/sessions
/user/sessions/new
/user/registrations
/user/registrations/new
/user/passwords
/user/passwords/new
/user/passwords/:id/edit
/user/passwords/:id
/user/profiles/edit
/user/profiles
/user/tokens/edit
/user/tokens
/task/lists/:list_id/items
/task/lists/:list_id/items/new
/task/lists/:list_id/items/:id/edit
/task/lists/:list_id/items/:id
/task/lists/:list_id/items/complete/:id
/task/lists/:list_id/items/incomplete/:id
/task/lists
/task/lists/new
/task/lists/:id/edit
/task/lists/:id

🤔 What was changed?

As we can see, the controllers and routes are organized more structuredly. Each main context has its own namespace (task, user), and the controllers are organized within it.

It is worth noting that the improvement in semantics is reflected in the routes, making them simpler and more readable.

🔎 What the next version will have?

Due to the improvement of the structure, the concept of base controllers will be introduced. In other words, controllers within a namespace can have specific (more cohesive) parent classes.

Next version: 031-resources-within-namespaces_base-controllers.

⬆ back to top


031-resources-within-namespaces_base-controllers

Lines of Code1355
Rubycritic Score91.56

In the branch 021-multi-controllers-per-entity_rest_actions_only, the TaskItemsConcern was introduced to share code among the task item controllers.

However, as with the previously introduced namespaces, this version introduces the concept of base controllers to replace the concern usage. This way, the controllers within a namespace can have specific (more cohesive) parent classes.

See below how the task controllers are organized:

app/controllers/task
├── items
│  ├── base_controller.rb
│  ├── complete_controller.rb
│  └── incomplete_controller.rb
└── items_controller.rb
Task::ItemsController < Task::Items::BaseController
Task::Items::CompletedController < Task::Items::BaseController
Task::Items::IncompleteController < Task::Items::BaseController

🤔 What are the benefits of using base controllers?

Since the previous version, we can see that the Rubycritic score has remained the same, which is positive given that the improvements in the structure do not complicate the existing implementation.

Although the score has not changed, we can see how this grouping reduces the effort to understand and find your way around the code. This also translates into increased cohesion, not at the class level but at the namespace level (groups of classes and modules).

🔎 What the next version will have?

The cohesion ideas (organization and grouping guided to a specific purpose) will be applied to views and partials. Check out to see the benefits of this approach.

Next version: 032-resources-within-namespaces_partials-grouped-by-context.

⬆ back to top


032-resources-within-namespaces_partials-grouped-by-context

Lines of Code1355
Rubycritic Score91.56

The previous version revealed the benefits of group controller within namespaces. This version will apply the same ideas to the view partials.

Let's see comparation between the previous and current structure:

Previous Current
app/views
├── shared
│  ├── settings
│  │  └── _header.html.erb
│  ├── tasks
│  │  ├── _add_new.html.erb
│  │  └── _header.html.erb
│  └── users
│     ├── _header.html.erb
│     ├── _reset_password_link.html.erb
│     ├── _sign_in_link.html.erb
│     ├── _sign_up_link.html.erb
│     └── user_token.json.jbuilder
└── task
   ├── items
   │  ├── _delete_action.html.erb
   │  ├── _edit_action.html.erb
   │  ├── _toggle_status_action.html.erb
   └── lists
      ├── _delete_action.html.erb
      ├── _edit_action.html.erb
      └── _view_items_action.html.erb
app/views
├── task
│  ├── items
│  │  ├── actions
│  │  │  ├── _delete.html.erb
│  │  │  ├── _edit.html.erb
│  │  │  └── _toggle_status.html.erb
│  ├── lists
│  │  ├── actions
│  │  │  ├── _delete.html.erb
│  │  │  ├── _edit.html.erb
│  │  │  └── _view_items.html.erb
│  └── shared
│     ├── _add_new.html.erb
│     └── _header.html.erb
└── user
   └── shared
      ├── _header.html.erb
      ├── links
      │  ├── _reset_password.html.erb
      │  ├── _sign_in.html.erb
      │  └── _sign_up.html.erb
      └── settings
         └── _header.html.erb

🤔 Why is this structure more cohesive than the previous one?

To answer this, let's analyze the partials in the app/views/shared folder from the previous version. It was less cohesive because it knew all the application contexts (settings, tasks, and users).

The current version shows that these partials have been moved to task or user contexts. This change created a more cohesive structure because of the lower indirection and greater specificity of each partial's use.

🔎 What the next version will have?

Aiming increasing the cohesion of the application, the next version will move the mailer views under the entity user context.

Next version: 033-resources-within-namespaces_mailers-under-entity-context.

⬆ back to top


033-resources-within-namespaces_mailers-under-entity-context

Lines of Code1356
Rubycritic Score91.56

This version continues system cohesion improvement by moving user mailer views from app/views/user_mailers to app/views/user/mailers.

app/views/user
├── mailers
│  ├── email_confirmation.html.erb
│  ├── email_confirmation.text.erb
│  ├── reset_password.html.erb
│  └── reset_password.text.erb
├── passwords/
├── profiles/
├── registrations/
├── sessions/
├── shared/
└── tokens/

🤔 Why is this structure more cohesive than the previous one?

Because the mailer views are now located under the user entity context.

🔎 What the next version will have?

Aiming to increase cohesion, the next version will add another nested namespace to isolate all user settings resources.

Next version: 034-resources-within-namespaces_nested-namespaces.

⬆ back to top


034-resources-within-namespaces_nested-namespaces

Lines of Code1356
Rubycritic Score91.56

This version pushes the cohesion further by creating another nested namespace (User::Settings).

Before After
app/views/user
├── mailers/
├── passwords/
├── profiles/
├── registrations/
├── sessions/
├── shared
│  ├── links
│  └── settings
└── tokens
app/controllers/user
├── passwords_controller.rb
├── profiles_controller.rb
├── registrations_controller.rb
├── sessions_controller.rb
└── tokens_controller.rb
app/views/user
├── mailers/
├── passwords/
├── registrations/
├── sessions/
├── settings
│  ├── profiles/
│  └── tokens/
└── shared
   └── links/
app/controllers/user
├── passwords_controller.rb
├── registrations_controller.rb
├── sessions_controller.rb
└── settings
   ├── profiles_controller.rb
   └── tokens_controller.rb

🤔 Why is this structure more cohesive than the previous one?

Because all user settings resources are isolated in the same namespace (User::Settings), which makes it easier to maintain and understand the codebase.

🔎 What the next version will have?

Aiming to improve the expressiveness of the application, the next version will make more use of singular resources.

Next version: 035-resources-within-namespaces_singular_resources.

⬆ back to top


035-resources-within-namespaces_singular_resources

Lines of Code1356
Rubycritic Score91.56

The definition of resources in the singular has been present since the first version (010).

What this branch does is make the declaration of resources consistent.

Previous
           Prefix Verb   URI Pattern                  Controller#Action
     user_session DELETE /user/session(.:format)      user/sessions#destroy
user_registration DELETE /user/registration(.:format) user/registrations#destroy
Current
            Prefix Verb   URI Pattern                   Controller#Action
     user_sessions DELETE /user/sessions(.:format)      user/sessions#destroy
user_registrations DELETE /user/registrations(.:format) user/registrations#destroy

🤔 Why does consistency matter?

Conceptual Integrity: In 1975, FredBrooks said: I will contend that Conceptual Integrity is the most important consideration in system design. It is better to have a system omit certain anomalous features and improvements, but to reflect one set of design ideas, than to have one that contains many good but independent and uncoordinated ideas.

Consistency is a key factor in the maintainability of a system. It makes it easier to understand and as a consequence, easier to maintain (promote changes).

This is applicable to everything in the system, from the code to the user interface.

This applies to everything in the system, from the code to the user interface. This branch was added to add this concept to the spotlight.

🔎 What the next version will have?

Aiming to improve the application consistency, the following version groups some models within namespaces.

Next version: 040-models-within-namespaces.

⬆ back to top


040-models-within-namespaces

Lines of Code1359
Rubycritic Score91.56

The previous versions already showed the benefits of organizing the codebase. This version goes further by grouping models within namespaces.

Beyond the code structure, check out the model's implementation to see how the associations reflect the namespace structure.

Here is the comparison of the models' directory structure (before and after):

Before After
app/models
├── account.rb
├── application_record.rb
├── current.rb
├── membership.rb
├── task_item.rb
├── task_list.rb
├── user.rb
└── user_token.rb
app/models
├── account.rb
├── application_record.rb
├── concerns
├── current.rb
├── membership.rb
├── task
│  ├── item.rb
│  └── list.rb
├── user
│  └── token.rb
└── user.rb

🤔 Why this change matter?

Cohesion + consistency = maintainability.

🔎 What the next version will have?

Seven iterations have been since version 021-multi-controllers-per-entity_rest_actions_only, but the Rubycritic score has remained the same (91.56).

But what was the reason?

The same controllers handle both the web application and the REST API. In other words, there needs to be more cohesion since each request format serves different purposes.

Because of this, the next version will perform this separation, and with this, it will be possible to determine whether or not this care in promoting cohesion will improve the quality score.

Next version: 050-separation-of-entry-points.

⬆ back to top


040-models-within-namespaces

Lines of Code1359
Rubycritic Score91.56

The previous versions already showed the benefits of organizing the codebase. This version goes further by grouping models within namespaces.

Beyond the code structure, check out the model's implementation to see how the associations reflect the namespace structure.

Here is the comparison of the models' directory structure (before and after):

Before After
app/models
├── account.rb
├── application_record.rb
├── current.rb
├── membership.rb
├── task_item.rb
├── task_list.rb
├── user.rb
└── user_token.rb
app/models
├── account.rb
├── application_record.rb
├── concerns
├── current.rb
├── membership.rb
├── task
│  ├── item.rb
│  └── list.rb
├── user
│  └── token.rb
└── user.rb

🤔 Why this change matter?

Cohesion + consistency = maintainability.

🔎 What the next version will have?

Seven iterations have been since version 021-multi-controllers-per-entity_rest_actions_only, but the Rubycritic score has remained the same (91.56).

But what was the reason?

The same controllers handle both the web application and the REST API. In other words, there needs to be more cohesion since each request format serves different purposes.

Because of this, the next version will perform this separation, and with this, it will be possible to determine whether or not this care in promoting cohesion will improve the quality score.

Next version: 050-separation-of-entry-points.

⬆ back to top


050-separation-of-entry-points

Lines of Code1462
Rubycritic Score94.04

This version shows a substantial increase in the Rubycritic score, from 91.56 to 94.04. The reason for this growth was the separation between the Web and REST API controllers and routes. Before that, both formats were handled by a single controller.

This separation of concerns reflects how cohesive each of these contexts has become.

See how the controllers and views are now organized:

Controllers

Web API::V1
app/controllers/web
├── base_controller.rb
├── task
│  ├── items
│  │  ├── base_controller.rb
│  │  ├── complete_controller.rb
│  │  └── incomplete_controller.rb
│  ├── items_controller.rb
│  └── lists_controller.rb
└── user
   ├── passwords_controller.rb
   ├── registrations_controller.rb
   ├── sessions_controller.rb
   └── settings
      ├── profiles_controller.rb
      └── tokens_controller.rb
app/controllers/api
└── v1
   ├── base_controller.rb
   ├── task
   │  ├── items
   │  │  ├── base_controller.rb
   │  │  ├── complete_controller.rb
   │  │  └── incomplete_controller.rb
   │  ├── items_controller.rb
   │  └── lists_controller.rb
   └── user
      ├── passwords
      │  └── resettings_controller.rb
      ├── passwords_controller.rb
      ├── registrations_controller.rb
      ├── sessions_controller.rb
      └── tokens_controller.rb

Views

Web API::V1
app/views/web
├── task
│  ├── items
│  │  ├── _form.html.erb
│  │  ├── actions
│  │  │  ├── _delete.html.erb
│  │  │  ├── _edit.html.erb
│  │  │  └── _toggle_status.html.erb
│  │  ├── edit.html.erb
│  │  ├── index.html.erb
│  │  ├── new.html.erb
│  │  └── show.html.erb
│  ├── lists
│  │  ├── _form.html.erb
│  │  ├── actions
│  │  │  ├── _delete.html.erb
│  │  │  ├── _edit.html.erb
│  │  │  └── _view_items.html.erb
│  │  ├── edit.html.erb
│  │  ├── index.html.erb
│  │  ├── new.html.erb
│  │  └── show.html.erb
│  └── shared
│     ├── _add_new.html.erb
│     └── _header.html.erb
└── user
   ├── passwords
   │  ├── edit.html.erb
   │  └── new.html.erb
   ├── registrations
   │  └── new.html.erb
   ├── sessions
   │  └── new.html.erb
   ├── settings
   │  ├── _header.html.erb
   │  ├── profiles
   │  │  └── edit.html.erb
   │  └── tokens
   │     └── edit.html.erb
   └── shared
      ├── _header.html.erb
      └── links
         ├── _reset_password.html.erb
         ├── _sign_in.html.erb
         └── _sign_up.html.erb
app/views/api
└── v1
   ├── errors
   │  ├── _response.json.jbuilder
   │  ├── from_model.json.jbuilder
   │  ├── response.json.jbuilder
   │  └── unauthorized.json.jbuilder
   ├── task
   │  ├── items
   │  │  ├── _record.json.jbuilder
   │  │  ├── index.json.jbuilder
   │  │  └── show.json.jbuilder
   │  └── lists
   │     ├── _record.json.jbuilder
   │     ├── index.json.jbuilder
   │     └── show.json.jbuilder
   └── user
      └── token.json.jbuilder

🤔 Why this change matter?

In addition to the increased cohesion, we can also see each context has the freedom to represent and organize its resources semantically.

For example, the web application uses the profile to update passwords. When we look at this resource, we see web/user/settings/profiles. However, the same responsibility was reflected differently in the API: api/v1/user/passwords.

This was unfeasible with the previous approach!

🔎 What the next version will have?

Apart from adding namespaces, the implementation of models has stayed the same so far.

Although this version improved the Rubycritic score significantly, it introduced duplication in controllers.

The next version will remove this duplication by concentrating logic in models.

Next version: 051-separation-of-entry-points_fat-models.

⬆ back to top


051-separation-of-entry-points_fat-models

Lines of Code1456
Rubycritic Score95.56

This version increases the Rubycritic score from 94.04 to 95.56 by moving the existing duplications to the models, the famous fat models and skinny controllers.

🤔 Why this change matter?

Because eliminating duplication generally increases maintenance.

But be careful: excessive and indiscriminate use of DRY (Don't Repeat Yourself) can compromise understanding and maintenance.

Try to create abstractions only to address real needs (real problems).

🔎 What the next version will have?

In the next version, we will enrich the application's domain model, starting with the Current class, which contains different responsibilities and a higher level of complexity.

Next version: 060-domain-model_account-member-poro.

⬆ back to top


060-domain-model_account-member-poro

Lines of Code1504
Rubycritic Score95.63

The Current class had two responsibilities: containing thread-safe shared state and queries to authorize user access.

This branch separates these responsibilities, keeping the primary scope of the Current class (containing thread-safe and shareable state) but moving the authorization responsibility to the Account::Member and Account::Member::Authorization POROS (Plain Old Ruby Objects).

POROS means a model that doesn't inherit from ActiveRecord, and it's a common pattern to extract complex logic from ActiveRecord models.

🤔 Why this change matter?

Cohesion + Separation of Concerns = Better understanding, maintainability and testability.

🔎 What the next version will have?

In the next version, we will enrich the application's domain model, starting with the Current class, which contains different responsibilities and a higher level of complexity.

Next version: 061-domain-model_user-token-poro.

⬆ back to top


061-domain-model_user-token-poro

Lines of Code1519
Rubycritic Score95.68

This branch introduces a PORO (Plain Old Ruby Objects) to handle the user token parsing, generation, and validation.

But wait, is this a good practice? Yes, it is. Extracting complex logic from ActiveRecord models is a common pattern. It is a recommendation present in the Rails documentation since version 3.1.0 (released in 2011).

The Model layer represents the domain model (such as Account, Product, Person, Post, etc.) and encapsulates the business logic specific to your application. In Rails, database-backed model classes are derived from ActiveRecord::Base. Active Record allows you to present the data from database rows as objects and embellish these data objects with business logic methods.

Although most Rails models are backed by a database, models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by the Active Model module.

Let me emphasize this part:

Models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by the Active Model module.

🤔 Why this change matter?

This change matters because it's a good practice to extract complex logic from ActiveRecord models. As a result, the Rubycritc score increased again, from 95.63 to 95.68.

🔎 What the next version will have?

The next version will isolate some strings into constants to reduce codebase duplication and fragility (weak references).

Next version: 062-domain-model_task-constants.

⬆ back to top


062-domain-model_task-constants

Lines of Code1526
Rubycritic Score95.78

This branch continues to enrich the domain model with a simple change. It ensures that the strings "completed" and "incomplete" are transformed into constants, Task::COMPLETED and Task::INCOMPLETE.

Note that this change also increases the Rubycritic score from 95.68 to 95.78.

🤔 Why this change matter?

Coupling is good when it is stable.

Before this change, breaking the behavior by committing a typo anywhere coupled to these strings would be possible. Now, using constants, we have a single reference for all usage points in the

🔎 What the next version will have?

The next iteration will extract complex operations from the models into specialized POROs.

Next version: 063-domain-model_user-operations.

⬆ back to top


063-domain-model_user-operations

Branch063-domain-model_user-operations
Lines of Code1563
Rubycritic Score95.77

This version isolates some user operations into specialized POROs (1). The goal here is to reduce the model's complexity.

Here's how the models are organized:

app/models
├── user
│  ├── account_deletion.rb
│  ├── password_resetting.rb
│  ├── registration.rb
│  ├── token
│  │  └── entity.rb
│  └── token.rb
└── user.rb

References: (1) https://dev.37signals.com/vanilla-rails-is-plenty/#what-about-services

🤔 Why this change matter?

Can you imagine a model with hundreds of lines of code? It's hard to maintain, right? By isolating some operations into specialized POROs, we can reduce the complexity and make things easier to maintain.

Beyond this, did you see that the file and folder structure reveals the domain model and what kind of operations that context can do?

This approach can distribute the complexity over specialized classes and, as a side effect, increase the codebase's understandability.

🔎 What the next version will have?

The next iteration will define the account context and decouple the user model from it to make the codebase even more orthogonal (orthogonality = the ability to change one thing without any unseen effect on other things).

Next version: 070-orthogonal-models.

⬆ back to top


070-orthogonal-models

Lines of Code1613
Rubycritic Score95.81

Orthogonality is the ability to change one thing without any unseen effect on other thing.

What this branch does is to decouple the User from the Account context to make the codebase even more orthogonal.

See how the two contexts are now separated:

Account User
app/models
├── account
│  ├── member
│  │  ├── authorization.rb
│  │  └── entity.rb
│  ├── member.rb
│  ├── membership.rb
│  ├── owner
│  │  ├── creation.rb
│  │  └── deletion.rb
│  ├── task
│  │  ├── item.rb
│  │  └── list.rb
│  └── task.rb
└── account.rb
app/models
├── user
│  ├── account_deletion.rb
│  ├── password_resetting.rb
│  ├── registration.rb
│  ├── token
│  │  └── entity.rb
│  └── token.rb
└── user.rb

🤔 Why this change matter?

The User model is now more focused on the user's behavior, while the Account model is more focused on the account's behavior.

This separation reduces the changes of undesired side effects when changing one of the models. This also happened when the Web and REST API resources were separated.

Another thing to notice is Rubycritic score which increased from 95.77 to 95.81, reflecting the high cohesion and low coupling of the codebase.

🔎 What the next version will have?

That's all folks! There is no other branch. 😉

After all these iterations, I hope you can see the enormous difference that focusing on cohesion and coupling can make in a codebase and how a framework like Rails (which has influenced so many others) is flexible enough to accommodate all these different approaches.

Ruby and Rails rocks! 🤘😎

⬆ back to top