├── docs ├── screenshots │ ├── 010_sign_in.jpg │ ├── 030_sign_up.jpg │ ├── 040_new_task.jpg │ ├── 070_sign_out.jpg │ ├── 045_edit_task.jpg │ ├── 050_task_lists.jpg │ ├── 011_sign_in_error.jpg │ ├── 041_task_created.jpg │ ├── 051_new_task_list.jpg │ ├── 020_forgot_password.jpg │ ├── 031_sign_up_errors.jpg │ ├── 032_sign_up_success.jpg │ ├── 042_task_completed.jpg │ ├── 043_tasks_completed.jpg │ ├── 044_tasks_incomplete.jpg │ ├── 052_task_list_created.jpg │ ├── 060_settings_profile.jpg │ └── 062_settings_api_token.jpg ├── 00_INSTALLATION.md ├── 02_WEB_APP_SCREENSHOTS.md ├── 01_REST_API_DOC.md └── 03_BRANCH_DESCRIPTIONS.md ├── .gitignore └── README.md /docs/screenshots/010_sign_in.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/010_sign_in.jpg -------------------------------------------------------------------------------- /docs/screenshots/030_sign_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/030_sign_up.jpg -------------------------------------------------------------------------------- /docs/screenshots/040_new_task.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/040_new_task.jpg -------------------------------------------------------------------------------- /docs/screenshots/070_sign_out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/070_sign_out.jpg -------------------------------------------------------------------------------- /docs/screenshots/045_edit_task.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/045_edit_task.jpg -------------------------------------------------------------------------------- /docs/screenshots/050_task_lists.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/050_task_lists.jpg -------------------------------------------------------------------------------- /docs/screenshots/011_sign_in_error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/011_sign_in_error.jpg -------------------------------------------------------------------------------- /docs/screenshots/041_task_created.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/041_task_created.jpg -------------------------------------------------------------------------------- /docs/screenshots/051_new_task_list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/051_new_task_list.jpg -------------------------------------------------------------------------------- /docs/screenshots/020_forgot_password.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/020_forgot_password.jpg -------------------------------------------------------------------------------- /docs/screenshots/031_sign_up_errors.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/031_sign_up_errors.jpg -------------------------------------------------------------------------------- /docs/screenshots/032_sign_up_success.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/032_sign_up_success.jpg -------------------------------------------------------------------------------- /docs/screenshots/042_task_completed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/042_task_completed.jpg -------------------------------------------------------------------------------- /docs/screenshots/043_tasks_completed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/043_tasks_completed.jpg -------------------------------------------------------------------------------- /docs/screenshots/044_tasks_incomplete.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/044_tasks_incomplete.jpg -------------------------------------------------------------------------------- /docs/screenshots/052_task_list_created.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/052_task_list_created.jpg -------------------------------------------------------------------------------- /docs/screenshots/060_settings_profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/060_settings_profile.jpg -------------------------------------------------------------------------------- /docs/screenshots/062_settings_api_token.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/architecture/rails-way-app/main/docs/screenshots/062_settings_api_token.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all environment files (except templates). 11 | /.env* 12 | !/.env*.erb 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore pidfiles, but keep the directory. 21 | /tmp/pids/* 22 | !/tmp/pids/ 23 | !/tmp/pids/.keep 24 | 25 | # Ignore storage (uploaded files in development and any SQLite databases). 26 | /storage/* 27 | !/storage/.keep 28 | /tmp/storage/* 29 | !/tmp/storage/ 30 | !/tmp/storage/.keep 31 | 32 | /public/assets 33 | 34 | # Ignore master key for decrypting credentials and more. 35 | /config/master.key 36 | 37 | /coverage/ 38 | -------------------------------------------------------------------------------- /docs/00_INSTALLATION.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | > `MENU` [README](../README.md) | **How to run locally** | [REST API doc](./01_REST_API_DOC.md) | [Web app screenshots](./02_WEB_APP_SCREENSHOTS.md) | [Branch descriptions](./03_BRANCH_DESCRIPTIONS.md) 4 | 5 | 6 | 7 | # 🚆 Rails Way App 8 | 9 | Instructions to setup and run the application locally. 10 | 11 | ## 📚 Table of contents 12 | 13 | - [System dependencies](#system-dependencies) 14 | - [How to setup the application](#how-to-setup-the-application) 15 | - [How to run the application locally](#how-to-run-the-application-locally) 16 | - [How to run the test suite (and generate coverage report)](#how-to-run-the-test-suite-and-generate-coverage-report) 17 | - [How to generate the code quality](#how-to-generate-the-code-quality) 18 | - [How to generate the App statistics](#how-to-generate-the-app-statistics) 19 | 20 | ## System dependencies 21 | * SQLite3 22 | * Ruby `3.3.4` 23 | * bundler `>= 2.5.14` 24 | 25 | ## How to setup the application 26 | 27 | 1. Install system dependencies 28 | 2. Access one of the [branches](../README.md#-repository-branches) 29 | 3. Create a `config/master.key` file with the following content: 30 | ```sh 31 | echo '257d6f71fb8c5a5e9724a130c0e35c5d' > config/master.key 32 | 33 | chmod 600 config/master.key 34 | ``` 35 | 3. Run `bin/setup` 36 | 37 |

⬆ back to top

38 | 39 | ## How to run the application locally 40 | 41 | 1. `bin/rails s` 42 | 2. Open in your browser: `http://localhost:3000` 43 | 44 |

⬆ back to top

45 | 46 | ## How to run the test suite (and generate coverage report) 47 | 48 | * `bin/rails test` 49 | 50 | ## How to generate the code quality 51 | 52 | * `bin/rails rubycritic` 53 | 54 | ## How to generate the App statistics 55 | 56 | * `bin/rails stats` 57 | 58 |

⬆ back to top

59 | -------------------------------------------------------------------------------- /docs/02_WEB_APP_SCREENSHOTS.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | > `MENU` [README](../README.md) | [How to run locally](./00_INSTALLATION.md) | [REST API doc](./01_REST_API_DOC.md) | Web app screenshots | [Branch descriptions](./03_BRANCH_DESCRIPTIONS.md) 4 | 5 | 6 | 7 | # 🚆 Rails Way App 8 | 9 | This document contains screenshots of web application pages. 10 | 11 | ## 📚 Table of contents 12 | 13 | - [Sign in](#sign-in) 14 | - [Forgot password](#forgot-password) 15 | - [Sign up](#sign-up) 16 | - [Tasks](#tasks) 17 | - [Task Lists](#task-lists) 18 | - [Settings](#settings) 19 | - [Sign out](#sign-out) 20 | 21 | 22 | ### Sign in 23 | 24 | 25 | 26 | 27 |

⬆ back to top

28 | 29 | ### Forgot password 30 | 31 | 32 | 33 |

⬆ back to top

34 | 35 | ### Sign up 36 | 37 | 38 | 39 | 40 | 41 |

⬆ back to top

42 | 43 | ### Tasks 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |

⬆ back to top

53 | 54 | ### Task Lists 55 | 56 | 57 | 58 | 59 | 60 |

⬆ back to top

61 | 62 | ### Settings 63 | 64 | 65 | 66 | 67 |

⬆ back to top

68 | 69 | ### Sign out 70 | 71 | 72 | 73 |

⬆ back to top

74 | -------------------------------------------------------------------------------- /docs/01_REST_API_DOC.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | > `MENU` [README](../README.md) | [How to run locally](./00_INSTALLATION.md) | **REST API doc** | [Web app screenshots](./02_WEB_APP_SCREENSHOTS.md) | [Branch descriptions](./03_BRANCH_DESCRIPTIONS.md) 4 | 5 | 6 | 7 | # 🚆 Rails Way App 8 | 9 | REST API documentation (cURL examples). 10 | 11 | ## Versions 12 | 13 | Although the REST API behavior is the same, the endpoints may vary depending on the branch. 14 | 15 | You can use the links below to access the documentation for the desired version. 16 | 17 | | LOC / GRADE | Branch | 18 | | ------------ | ------ | 19 | | 1326 / 89.23 | [010-one-controller-per-entity](https://github.com/solid-process/rails-way-app/blob/010-one-controller-per-entity/docs/01_REST_API_DOC.md#-table-of-contents-) | 20 | | 1350 / 90.34 | [011-one-controller-per-entity_user-concerns](https://github.com/solid-process/rails-way-app/blob/011-one-controller-per-entity_user-concerns/docs/01_REST_API_DOC.md#-table-of-contents-) | 21 | | 1342 / 91.34 | [020-multi-controllers-per-entity](https://github.com/solid-process/rails-way-app/blob/020-multi-controllers-per-entity/docs/01_REST_API_DOC.md#-table-of-contents-) | 22 | | 1361 / 91.56 | [021-multi-controllers-per-entity_rest-actions-only](https://github.com/solid-process/rails-way-app/blob/021-multi-controllers-per-entity_rest-actions-only/docs/01_REST_API_DOC.md#-table-of-contents-) | 23 | | 1361 / 91.56 | [030-resources-within-namespaces](https://github.com/solid-process/rails-way-app/blob/030-resources-within-namespaces/docs/01_REST_API_DOC.md#-table-of-contents-) | 24 | | 1355 / 91.56 | [031-resources-within-namespaces_base-controllers](https://github.com/solid-process/rails-way-app/blob/031-resources-within-namespaces_base-controllers/docs/01_REST_API_DOC.md#-table-of-contents-) | 25 | | 1355 / 91.56 | [032-resources-within-namespaces_partials-grouped-by-context](https://github.com/solid-process/rails-way-app/blob/032-resources-within-namespaces_partials-grouped-by-context/docs/01_REST_API_DOC.md#-table-of-contents-) | 26 | | 1356 / 91.56 | [033-resources-within-namespaces_mailers-under-entity-context](https://github.com/solid-process/rails-way-app/blob/033-resources-within-namespaces_mailers-under-entity-context/docs/01_REST_API_DOC.md#-table-of-contents-) | 27 | | 1356 / 91.56 | [034-resources-within-namespaces_nested-namespaces](https://github.com/solid-process/rails-way-app/blob/034-resources-within-namespaces_nested-namespaces/docs/01_REST_API_DOC.md#-table-of-contents-) | 28 | | 1356 / 91.56 | [035-resources-within-namespaces_singular_resources](https://github.com/solid-process/rails-way-app/blob/035-resources-within-namespaces_singular_resources/docs/01_REST_API_DOC.md#-table-of-contents-) | 29 | | 1359 / 91.56 | [040-models-within-namespaces](https://github.com/solid-process/rails-way-app/blob/040-models-within-namespaces/docs/01_REST_API_DOC.md#-table-of-contents-) | 30 | | 1462 / 94.04 | [050-separation-of-entry-points](https://github.com/solid-process/rails-way-app/blob/050-separation-of-entry-points/docs/01_REST_API_DOC.md#-table-of-contents-) | 31 | | 1456 / 95.56 | [051-separation-of-entry-points_fat-models](https://github.com/solid-process/rails-way-app/blob/051-separation-of-entry-points_fat-models/docs/01_REST_API_DOC.md#-table-of-contents-) | 32 | | 1504 / 95.63 | [060-domain-model_account-member-poro](https://github.com/solid-process/rails-way-app/blob/060-domain-model_account-member-poro/docs/01_REST_API_DOC.md#-table-of-contents-) | 33 | | 1519 / 95.68 | [061-domain-model_user-token-poro](https://github.com/solid-process/rails-way-app/blob/061-domain-model_user-token-poro/docs/01_REST_API_DOC.md#-table-of-contents-) | 34 | | 1526 / 95.78 | [062-domain-model_task-constants](https://github.com/solid-process/rails-way-app/blob/062-domain-model_task-constants/docs/01_REST_API_DOC.md#-table-of-contents-) | 35 | | 1563 / 95.77 | [063-domain-model_user-operations](https://github.com/solid-process/rails-way-app/blob/063-domain-model_user-operations/docs/01_REST_API_DOC.md#-table-of-contents-) | 36 | | 1613 / 95.81 | [070-orthogonal-models](https://github.com/solid-process/rails-way-app/blob/070-orthogonal-models/docs/01_REST_API_DOC.md#-table-of-contents-) | 37 | 38 | The following commands were used to generate the LOC and GRADE reports: 39 | - **LOC** (lines of code): `bin/rails stats` 40 | - **GRADE** (code quality): `bin/rails rubycritic` 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | > `MENU` **README** | [How to run locally](./docs/00_INSTALLATION.md) | [REST API doc](./docs/01_REST_API_DOC.md) | [Web app screenshots](./docs/02_WEB_APP_SCREENSHOTS.md) | [Branch descriptions](./docs/03_BRANCH_DESCRIPTIONS.md) 4 | 5 | 6 | 7 | # 🚆 Rails Way App 8 | 9 | _**Eighteen versions**_ (gradually implemented) of a Web and REST API app made with [Ruby on Rails](https://guides.rubyonrails.org/) that aims to get the most out of the `MVC`/`Rails Way`. 10 | 11 | ## 📚 Table of contents 12 | 13 | - [📢 Disclaimer](#-disclaimer) 14 | - [🙌 Repository branches](#-repository-branches) 15 | - [👋 About](#-about) 16 | 17 | ## 📢 Disclaimer 18 | 19 | [Ruby on Rails](https://rubyonrails.org/) is a highly productive MVC framework whose [primary value proposition is to be a one-person framework](https://www.youtube.com/watch?v=iqXjGiQ_D-A). In other words, to empower and make individuals as productive as entire teams. 20 | 21 | However, this proposal applies not only to individuals but also to giant teams. [Shopify](https://shopify.engineering/), for example, has thousands of developers working on a monolithic and modular application with millions of lines of code. 22 | 23 | **The main challenge of any medium to colossal system is to accommodate its complexity well**. As the code grows, we need to have the freedom and capacity to separate responsibilities in the best possible way. 24 | 25 | The project's main objective is to demonstrate different approaches to improving the design of a Rails application without compromising its conventions and structure. 26 | 27 | It is a `Web` and `REST API` app with over `4,000` lines of code (implementation + tests) and was implemented in **18 versions**. This is to gradually demonstrate the pros and cons of each approach. Enjoy! ✌️😊 28 | 29 |

⬆ back to top

30 | 31 | ## 🙌 Repository branches 32 | 33 | This repository has **eighteen** branches that represent the application's evolution. 34 | 35 | Every branch contains a `README.md` which explains the changes made in the codebase. However, you can access the documentation of all branches in the [`docs/03_BRANCH_DESCRIPTIONS.md`](./docs/03_BRANCH_DESCRIPTIONS.md) file. 36 | 37 | | LOC / GRADE | Branch | 38 | | ------------ | ------ | 39 | | 1326 / 89.23 | [010-one-controller-per-entity](https://github.com/solid-process/rails-way-app/blob/010-one-controller-per-entity?tab=readme-ov-file#-rails-way-app-) | 40 | | 1350 / 90.34 | [011-one-controller-per-entity_user-concerns](https://github.com/solid-process/rails-way-app/blob/011-one-controller-per-entity_user-concerns?tab=readme-ov-file#-rails-way-app-) | 41 | | 1342 / 91.34 | [020-multi-controllers-per-entity](https://github.com/solid-process/rails-way-app/blob/020-multi-controllers-per-entity?tab=readme-ov-file#-rails-way-app-) | 42 | | 1361 / 91.56 | [021-multi-controllers-per-entity_rest-actions-only](https://github.com/solid-process/rails-way-app/blob/021-multi-controllers-per-entity_rest-actions-only?tab=readme-ov-file#-rails-way-app-) | 43 | | 1361 / 91.56 | [030-resources-within-namespaces](https://github.com/solid-process/rails-way-app/blob/030-resources-within-namespaces?tab=readme-ov-file#-rails-way-app-) | 44 | | 1355 / 91.56 | [031-resources-within-namespaces_base-controllers](https://github.com/solid-process/rails-way-app/blob/031-resources-within-namespaces_base-controllers?tab=readme-ov-file#-rails-way-app-) | 45 | | 1355 / 91.56 | [032-resources-within-namespaces_partials-grouped-by-context](https://github.com/solid-process/rails-way-app/blob/032-resources-within-namespaces_partials-grouped-by-context?tab=readme-ov-file#-rails-way-app-) | 46 | | 1356 / 91.56 | [033-resources-within-namespaces_mailers-under-entity-context](https://github.com/solid-process/rails-way-app/blob/033-resources-within-namespaces_mailers-under-entity-context?tab=readme-ov-file#-rails-way-app-) | 47 | | 1356 / 91.56 | [034-resources-within-namespaces_nested-namespaces](https://github.com/solid-process/rails-way-app/blob/034-resources-within-namespaces_nested-namespaces?tab=readme-ov-file#-rails-way-app-) | 48 | | 1356 / 91.56 | [035-resources-within-namespaces_singular_resources](https://github.com/solid-process/rails-way-app/blob/035-resources-within-namespaces_singular_resources?tab=readme-ov-file#-rails-way-app-) | 49 | | 1359 / 91.56 | [040-models-within-namespaces](https://github.com/solid-process/rails-way-app/blob/040-models-within-namespaces?tab=readme-ov-file#-rails-way-app-) | 50 | | 1462 / 94.04 | [050-separation-of-entry-points](https://github.com/solid-process/rails-way-app/blob/050-separation-of-entry-points?tab=readme-ov-file#-rails-way-app-) | 51 | | 1456 / 95.56 | [051-separation-of-entry-points_fat-models](https://github.com/solid-process/rails-way-app/blob/051-separation-of-entry-points_fat-models?tab=readme-ov-file#-rails-way-app-) | 52 | | 1504 / 95.63 | [060-domain-model_account-member-poro](https://github.com/solid-process/rails-way-app/blob/060-domain-model_account-member-poro?tab=readme-ov-file#-rails-way-app-) | 53 | | 1519 / 95.68 | [061-domain-model_user-token-poro](https://github.com/solid-process/rails-way-app/blob/061-domain-model_user-token-poro?tab=readme-ov-file#-rails-way-app-) | 54 | | 1526 / 95.78 | [062-domain-model_task-constants](https://github.com/solid-process/rails-way-app/blob/062-domain-model_task-constants?tab=readme-ov-file#-rails-way-app-) | 55 | | 1563 / 95.77 | [063-domain-model_user-operations](https://github.com/solid-process/rails-way-app/blob/063-domain-model_user-operations?tab=readme-ov-file#-rails-way-app-) | 56 | | 1613 / 95.81 | [070-orthogonal-models](https://github.com/solid-process/rails-way-app/blob/070-orthogonal-models?tab=readme-ov-file#-rails-way-app-) | 57 | 58 | The following commands were used to generate the LOC and GRADE reports: 59 | - **LOC** (lines of code): `bin/rails stats` 60 | - **GRADE** (code quality): `bin/rails rubycritic` 61 | 62 |

⬆ back to top

63 | 64 | ## 👋 About 65 | 66 | [Rodrigo Serradura](https://rodrigoserradura.com/) created this project. He is the creator of Solid Process, among other similar projects, such as [solid-rails-app](https://github.com/solid-process/solid-rails-app), which consists of demonstrating (in `12 gradual versions`) how the implementation of processes as code (based on the concept of use cases) can add value to a Ruby and Rails codebase. 67 | 68 | In the Rails community, we have people at different stages of their careers and companies in various phases (validating ideas, refining, scaling products); the goal here is to help them on their journey. By sharing knowledge and practical references. 69 | 70 | Ruby and the Rails framework is excellent, and my mission here is to try to add value to such great tools (Ruby and Rails rocks!!! 🤘😎). 71 | 72 |

⬆ back to top

73 | -------------------------------------------------------------------------------- /docs/03_BRANCH_DESCRIPTIONS.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | > `MENU` [README](../README.md) | [How to run locally](./00_INSTALLATION.md) | [REST API doc](./01_REST_API_DOC.md) | [Web app screenshots](./02_WEB_APP_SCREENSHOTS.md) | Branch descriptions 4 | 5 | 6 | 7 | # 🚆 Rails Way App 8 | 9 | This document presents the branches of the Rails Way App project. 10 | 11 | ## 📚 Table of contents 12 | - [010-one-controller-per-entity](#010-one-controller-per-entity) 13 | - [011-one-controller-per-entity\_user-concerns](#011-one-controller-per-entity_user-concerns) 14 | - [020-multi-controllers-per-entity](#020-multi-controllers-per-entity) 15 | - [021-multi-controllers-per-entity\_rest-actions-only](#021-multi-controllers-per-entity_rest-actions-only) 16 | - [030-resources-within-namespaces](#030-resources-within-namespaces) 17 | - [031-resources-within-namespaces\_base-controllers](#031-resources-within-namespaces_base-controllers) 18 | - [032-resources-within-namespaces\_partials-grouped-by-context](#032-resources-within-namespaces_partials-grouped-by-context) 19 | - [033-resources-within-namespaces\_mailers-under-entity-context](#033-resources-within-namespaces_mailers-under-entity-context) 20 | - [034-resources-within-namespaces\_nested-namespaces](#034-resources-within-namespaces_nested-namespaces) 21 | - [035-resources-within-namespaces\_singular\_resources](#035-resources-within-namespaces_singular_resources) 22 | - [040-models-within-namespaces](#040-models-within-namespaces) 23 | - [050-separation-of-entry-points](#050-separation-of-entry-points) 24 | - [051-separation-of-entry-points\_fat-models](#051-separation-of-entry-points_fat-models) 25 | - [060-domain-model\_account-member-poro](#060-domain-model_account-member-poro) 26 | - [061-domain-model\_user-token-poro](#061-domain-model_user-token-poro) 27 | - [062-domain-model\_task-constants](#062-domain-model_task-constants) 28 | - [063-domain-model\_user-operations](#063-domain-model_user-operations) 29 | - [070-orthogonal-models](#070-orthogonal-models) 30 | 31 | --- 32 | 33 | ### 010-one-controller-per-entity 34 | 35 | 36 | 37 | 38 |
Lines of Code1326
Rubycritic Score89.23
39 | 40 | In this version, a single controller is used for each main entity of the system (model). 41 | 42 | 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. 43 | 44 | 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. 45 | 46 | ```sh 47 | 110 app/controllers/application_controller.rb 48 | 130 app/controllers/task_items_controller.rb 49 | 82 app/controllers/task_lists_controller.rb 50 | 206 app/controllers/users_controller.rb 51 | 528 total 52 | ``` 53 | 54 | #### 🤔 What is the problem with low cohesion code? 55 | 56 | Low cohesion leads to greater coupling, higher costs, and efforts to promote changes. 57 | 58 | #### 🔎 What the next version will have? 59 | 60 | 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. 61 | 62 | `Next version:` [011-one-controller-per-entity_user-concerns](https://github.com/solid-process/rails-way-app/tree/011-one-controller-per-entity_user-concerns?tab=readme-ov-file) 63 | 64 |

⬆ back to top

65 | 66 | --- 67 | 68 | ### 011-one-controller-per-entity_user-concerns 69 | 70 | 71 | 72 | 73 |
Lines of Code1350
Rubycritic Score90.34
74 | 75 | **Refactoring with ActiveSupport::Concern:** 76 | 77 | This is how this [feature is presented in the Rails Guides](https://guides.rubyonrails.org/getting_started.html#using-concerns): 78 | 79 | > 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. 80 | 81 | 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: 82 | 83 | ```sh 84 | 21 app/controllers/users_controller.rb 85 | 48 app/controllers/concerns/user_passwords_concern.rb 86 | 41 app/controllers/concerns/user_profiles_concern.rb 87 | 48 app/controllers/concerns/user_registrations_concern.rb 88 | 49 app/controllers/concerns/user_sessions_concern.rb 89 | 38 app/controllers/concerns/user_tokens_concern.rb 90 | 245 total 91 | ``` 92 | 93 | We can see a positive impact on the Rubycritic score, which went from `89.23` to `90.24`. 94 | 95 | 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...). 96 | 97 | 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. 98 | 99 | #### 🤔 Would it be possible to achieve this separation and avoid this collision? 100 | 101 | The answer is _**yes**_! 🙌 102 | 103 | #### 🔎 What the next version will have? 104 | 105 | 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. 106 | 107 | `Next version`: [020-multi-controllers-per-entity](https://github.com/solid-process/rails-way-app/tree/020-multi-controllers-per-entity?tab=readme-ov-file). 108 | 109 |

⬆ back to top

110 | 111 | --- 112 | 113 | ### 020-multi-controllers-per-entity 114 | 115 | 116 | 117 | 118 |
Lines of Code1342
Rubycritic Score91.34
119 | 120 | The previous version demonstrates how concerns can help safely move code around, facilitating a better understanding of the different responsibilities in an implementation. 121 | 122 | These were the created concerns: 123 | - `UserRegistrationsConcern` 124 | - `UserSessionsConcern` 125 | - `UserPasswordsConcern` 126 | - `UserTokensConcern` 127 | - `UserProfilesConcern` 128 | 129 | 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. 130 | 131 | And here is what this version does. It uses the concerns categorization to implement dedicated routes and controllers. 132 | 133 | See how the controllers turned out: 134 | 135 | ```sh 136 | 110 app/controllers/application_controller.rb 137 | 130 app/controllers/task_items_controller.rb 138 | 82 app/controllers/task_lists_controller.rb 139 | 60 app/controllers/user_passwords_controller.rb 140 | 41 app/controllers/user_profiles_controller.rb 141 | 49 app/controllers/user_registrations_controller.rb 142 | 50 app/controllers/user_sessions_controller.rb 143 | 25 app/controllers/user_tokens_controller.rb 144 | 547 total 145 | ``` 146 | 147 | #### 🤔 What was changed? 148 | 149 | The Rubycritic score increased from `90.34` to `91.34`. 150 | 151 | 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`...). 152 | 153 | 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). 154 | 155 | #### 🔎 What the next version will have? 156 | 157 | It shows how the restriction of REST actions can enforce cohesion and ensure controllers are responsible for specific contexts/concepts. 158 | 159 | `Next version`: [021-multi-controllers-per-entity_rest-actions-only](https://github.com/solid-process/rails-way-app/tree/021-multi-controllers-per-entity_rest-actions-only?tab=readme-ov-file). 160 | 161 |

⬆ back to top

162 | 163 | --- 164 | 165 | ### 021-multi-controllers-per-entity_rest-actions-only 166 | 167 | 168 | 169 | 170 |
Lines of Code1361
Rubycritic Score91.56
171 | 172 | This version ensures that all controllers only have REST actions. 173 | 174 | To accomplish this the `task_items#complete` and `task_items#incomplete` actions were moved to their own controller: 175 | 176 | | From | To | 177 | | -------------------------------- | -------------------------------------- | 178 | | `TaskItemsController#complete` | `CompleteTaskItemsController#update` | 179 | | `TaskItemsController#incomplete` | `IncompleteTaskItemsController#update` | 180 | 181 | Beyond this change, concern was created to share code between the `CompleteTaskItemsController,` `IncompleteTaskItemsController`, and `TaskItemsController.` 182 | 183 | See how the task items controllers are now: 184 | 185 | ```sh 186 | app/controllers/ 187 | ├── concerns/ 188 | │ └── task_items_concern.rb 189 | ├── complete_task_items_controller.rb 190 | ├── incomplete_task_items_controller.rb 191 | └── task_items_controller.rb 192 | ``` 193 | 194 | #### 🤔 What was changed? 195 | 196 | The Rubycritic score increased from `91.34` to `91.56`. 197 | 198 | This happened because each cohesion has been increased, and the controllers are more specialized. 199 | 200 | But lets be honest, the routes are worse than before. 😅 201 | 202 | #### 🔎 What the next version will have? 203 | 204 | Let's do what DHH taught us over a decade ago: https://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/ 205 | 206 | This will improve the routes and put the controllers in a better structure. 207 | 208 | `Next version`: [030-resources-within-namespaces](https://github.com/solid-process/rails-way-app/tree/030-resources-within-namespaces?tab=readme-ov-file). 209 | 210 |

⬆ back to top

211 | 212 | --- 213 | 214 | ### 030-resources-within-namespaces 215 | 216 | 217 | 218 | 219 |
Lines of Code1361
Rubycritic Score91.56
220 | 221 | 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. 222 | 223 | **Controllers:** 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 247 | 266 | 267 |
BeforeAfter
232 |
 233 | app/controllers
 234 | ├── concerns
 235 | │  └── task_items_concern.rb
 236 | ├── application_controller.rb
 237 | ├── complete_task_items_controller.rb
 238 | ├── incomplete_task_items_controller.rb
 239 | ├── task_items_controller.rb
 240 | ├── task_lists_controller.rb
 241 | ├── user_passwords_controller.rb
 242 | ├── user_profiles_controller.rb
 243 | ├── user_registrations_controller.rb
 244 | ├── user_sessions_controller.rb
 245 | └── user_tokens_controller.rb
246 |
248 |
 249 | app/controllers
 250 | ├── concerns
 251 | │  └── task_items_concern.rb
 252 | ├── application_controller.rb
 253 | ├── task
 254 | │  ├── items
 255 | │  │  ├── complete_controller.rb
 256 | │  │  └── incomplete_controller.rb
 257 | │  ├── items_controller.rb
 258 | │  └── lists_controller.rb
 259 | └── user
 260 |    ├── passwords_controller.rb
 261 |    ├── profiles_controller.rb
 262 |    ├── registrations_controller.rb
 263 |    ├── sessions_controller.rb
 264 |    └── tokens_controller.rb
265 |
268 | 269 | **Routes** 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 302 | 327 | 328 |
BeforeAfter
278 |
 279 | /user_sessions
 280 | /user_sessions/new
 281 | /user_registrations
 282 | /user_registrations/new
 283 | /user_passwords
 284 | /user_passwords/new
 285 | /user_passwords/:id/edit
 286 | /user_passwords/:id
 287 | /user_profiles/edit
 288 | /user_profiles
 289 | /user_tokens/edit
 290 | /user_tokens
 291 | /task_lists/:task_list_id/task_items
 292 | /task_lists/:task_list_id/task_items/new
 293 | /task_lists/:task_list_id/task_items/:id/edit
 294 | /task_lists/:task_list_id/task_items/:id
 295 | /task_lists/:task_list_id/complete_task_items/:id
 296 | /task_lists/:task_list_id/incomplete_task_items/:id
 297 | /task_lists
 298 | /task_lists/new
 299 | /task_lists/:id/edit
 300 | /task_lists/:id
301 |
303 |
 304 | /user/sessions
 305 | /user/sessions/new
 306 | /user/registrations
 307 | /user/registrations/new
 308 | /user/passwords
 309 | /user/passwords/new
 310 | /user/passwords/:id/edit
 311 | /user/passwords/:id
 312 | /user/profiles/edit
 313 | /user/profiles
 314 | /user/tokens/edit
 315 | /user/tokens
 316 | /task/lists/:list_id/items
 317 | /task/lists/:list_id/items/new
 318 | /task/lists/:list_id/items/:id/edit
 319 | /task/lists/:list_id/items/:id
 320 | /task/lists/:list_id/items/complete/:id
 321 | /task/lists/:list_id/items/incomplete/:id
 322 | /task/lists
 323 | /task/lists/new
 324 | /task/lists/:id/edit
 325 | /task/lists/:id
326 |
329 | 330 | #### 🤔 What was changed? 331 | 332 | 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. 333 | 334 | It is worth noting that the improvement in semantics is reflected in the routes, making them simpler and more readable. 335 | 336 | #### 🔎 What the next version will have? 337 | 338 | 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. 339 | 340 | `Next version`: [031-resources-within-namespaces_base-controllers](https://github.com/solid-process/rails-way-app/tree/031-resources-within-namespaces_base-controllers?tab=readme-ov-file). 341 | 342 |

⬆ back to top

343 | 344 | --- 345 | 346 | ### 031-resources-within-namespaces_base-controllers 347 | 348 | 349 | 350 | 351 |
Lines of Code1355
Rubycritic Score91.56
352 | 353 | In the branch `021-multi-controllers-per-entity_rest_actions_only`, the `TaskItemsConcern` was introduced to share code among the task item controllers. 354 | 355 | 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. 356 | 357 | See below how the task controllers are organized: 358 | 359 | ```sh 360 | app/controllers/task 361 | ├── items 362 | │ ├── base_controller.rb 363 | │ ├── complete_controller.rb 364 | │ └── incomplete_controller.rb 365 | └── items_controller.rb 366 | ``` 367 | 368 | ```ruby 369 | Task::ItemsController < Task::Items::BaseController 370 | Task::Items::CompletedController < Task::Items::BaseController 371 | Task::Items::IncompleteController < Task::Items::BaseController 372 | ``` 373 | 374 | #### 🤔 What are the benefits of using base controllers? 375 | 376 | 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. 377 | 378 | 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). 379 | 380 | #### 🔎 What the next version will have? 381 | 382 | 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. 383 | 384 | `Next version`: [032-resources-within-namespaces_partials-grouped-by-context](https://github.com/solid-process/rails-way-app/tree/032-resources-within-namespaces_partials-grouped-by-context?tab=readme-ov-file). 385 | 386 |

⬆ back to top

387 | 388 | --- 389 | 390 | ### 032-resources-within-namespaces_partials-grouped-by-context 391 | 392 | 393 | 394 | 395 |
Lines of Code1355
Rubycritic Score91.56
396 | 397 | The previous version revealed the benefits of group controller within namespaces. This version will apply the same ideas to the view partials. 398 | 399 | Let's see comparation between the previous and current structure: 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 432 | 459 | 460 |
PreviousCurrent
408 |
 409 | app/views
 410 | ├── shared
 411 | │  ├── settings
 412 | │  │  └── _header.html.erb
 413 | │  ├── tasks
 414 | │  │  ├── _add_new.html.erb
 415 | │  │  └── _header.html.erb
 416 | │  └── users
 417 | │     ├── _header.html.erb
 418 | │     ├── _reset_password_link.html.erb
 419 | │     ├── _sign_in_link.html.erb
 420 | │     ├── _sign_up_link.html.erb
 421 | │     └── user_token.json.jbuilder
 422 | └── task
 423 |    ├── items
 424 |    │  ├── _delete_action.html.erb
 425 |    │  ├── _edit_action.html.erb
 426 |    │  ├── _toggle_status_action.html.erb
 427 |    └── lists
 428 |       ├── _delete_action.html.erb
 429 |       ├── _edit_action.html.erb
 430 |       └── _view_items_action.html.erb
431 |
433 |
 434 | app/views
 435 | ├── task
 436 | │  ├── items
 437 | │  │  ├── actions
 438 | │  │  │  ├── _delete.html.erb
 439 | │  │  │  ├── _edit.html.erb
 440 | │  │  │  └── _toggle_status.html.erb
 441 | │  ├── lists
 442 | │  │  ├── actions
 443 | │  │  │  ├── _delete.html.erb
 444 | │  │  │  ├── _edit.html.erb
 445 | │  │  │  └── _view_items.html.erb
 446 | │  └── shared
 447 | │     ├── _add_new.html.erb
 448 | │     └── _header.html.erb
 449 | └── user
 450 |    └── shared
 451 |       ├── _header.html.erb
 452 |       ├── links
 453 |       │  ├── _reset_password.html.erb
 454 |       │  ├── _sign_in.html.erb
 455 |       │  └── _sign_up.html.erb
 456 |       └── settings
 457 |          └── _header.html.erb
458 |
461 | 462 | #### 🤔 Why is this structure more cohesive than the previous one? 463 | 464 | 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). 465 | 466 | 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. 467 | 468 | #### 🔎 What the next version will have? 469 | 470 | Aiming increasing the cohesion of the application, the next version will move the mailer views under the entity user context. 471 | 472 | `Next version`: [033-resources-within-namespaces_mailers-under-entity-context](https://github.com/solid-process/rails-way-app/tree/033-resources-within-namespaces_mailers-under-entity-context?tab=readme-ov-file). 473 | 474 |

⬆ back to top

475 | 476 | --- 477 | 478 | ### 033-resources-within-namespaces_mailers-under-entity-context 479 | 480 | 481 | 482 | 483 |
Lines of Code1356
Rubycritic Score91.56
484 | 485 | This version continues system cohesion improvement by moving user mailer views from app/views/user_mailers to app/views/user/mailers. 486 | 487 | ```sh 488 | app/views/user 489 | ├── mailers 490 | │ ├── email_confirmation.html.erb 491 | │ ├── email_confirmation.text.erb 492 | │ ├── reset_password.html.erb 493 | │ └── reset_password.text.erb 494 | ├── passwords/ 495 | ├── profiles/ 496 | ├── registrations/ 497 | ├── sessions/ 498 | ├── shared/ 499 | └── tokens/ 500 | ``` 501 | 502 | #### 🤔 Why is this structure more cohesive than the previous one? 503 | 504 | Because the mailer views are now located under the user entity context. 505 | 506 | #### 🔎 What the next version will have? 507 | 508 | Aiming to increase cohesion, the next version will add another nested namespace to isolate all user settings resources. 509 | 510 | `Next version`: [034-resources-within-namespaces_nested-namespaces](https://github.com/solid-process/rails-way-app/tree/034-resources-within-namespaces_nested-namespaces?tab=readme-ov-file). 511 | 512 |

⬆ back to top

513 | 514 | --- 515 | 516 | ### 034-resources-within-namespaces_nested-namespaces 517 | 518 | 519 | 520 | 521 |
Lines of Code1356
Rubycritic Score91.56
522 | 523 | This version pushes the cohesion further by creating another nested namespace (`User::Settings`). 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 550 | 570 | 571 |
BeforeAfter
532 |
 533 | app/views/user
 534 | ├── mailers/
 535 | ├── passwords/
 536 | ├── profiles/
 537 | ├── registrations/
 538 | ├── sessions/
 539 | ├── shared
 540 | │  ├── links
 541 | │  └── settings
 542 | └── tokens
 543 | app/controllers/user
 544 | ├── passwords_controller.rb
 545 | ├── profiles_controller.rb
 546 | ├── registrations_controller.rb
 547 | ├── sessions_controller.rb
 548 | └── tokens_controller.rb
549 |
551 |
 552 | app/views/user
 553 | ├── mailers/
 554 | ├── passwords/
 555 | ├── registrations/
 556 | ├── sessions/
 557 | ├── settings
 558 | │  ├── profiles/
 559 | │  └── tokens/
 560 | └── shared
 561 |    └── links/
 562 | app/controllers/user
 563 | ├── passwords_controller.rb
 564 | ├── registrations_controller.rb
 565 | ├── sessions_controller.rb
 566 | └── settings
 567 |    ├── profiles_controller.rb
 568 |    └── tokens_controller.rb
569 |
572 | 573 | #### 🤔 Why is this structure more cohesive than the previous one? 574 | 575 | Because all user settings resources are isolated in the same namespace (`User::Settings`), which makes it easier to maintain and understand the codebase. 576 | 577 | #### 🔎 What the next version will have? 578 | 579 | Aiming to improve the expressiveness of the application, the next version will make more use of singular resources. 580 | 581 | `Next version`: [035-resources-within-namespaces_singular_resources](https://github.com/solid-process/rails-way-app/tree/035-resources-within-namespaces_singular_resources?tab=readme-ov-file). 582 | 583 |

⬆ back to top

584 | 585 | --- 586 | 587 | ### 035-resources-within-namespaces_singular_resources 588 | 589 | 590 | 591 | 592 |
Lines of Code1356
Rubycritic Score91.56
593 | 594 | The definition of resources in the singular has been present since the first version (`010`). 595 | 596 | What this branch does is make the declaration of resources consistent. 597 | 598 | 599 | 600 | 601 | 607 | 608 | 609 | 610 | 616 | 617 |
Previous 602 |
 603 |            Prefix Verb   URI Pattern                  Controller#Action
 604 |      user_session DELETE /user/session(.:format)      user/sessions#destroy
 605 | user_registration DELETE /user/registration(.:format) user/registrations#destroy
606 |
Current 611 |
 612 |             Prefix Verb   URI Pattern                   Controller#Action
 613 |      user_sessions DELETE /user/sessions(.:format)      user/sessions#destroy
 614 | user_registrations DELETE /user/registrations(.:format) user/registrations#destroy
615 |
618 | 619 | #### 🤔 Why does consistency matter? 620 | 621 | > Conceptual Integrity: 622 | > 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. 623 | 624 | 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). 625 | 626 | This is applicable to everything in the system, from the code to the user interface. 627 | 628 | 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. 629 | 630 | #### 🔎 What the next version will have? 631 | 632 | Aiming to improve the application consistency, the following version groups some models within namespaces. 633 | 634 | `Next version`: [040-models-within-namespaces](https://github.com/solid-process/rails-way-app/tree/040-models-within-namespaces?tab=readme-ov-file). 635 | 636 |

⬆ back to top

637 | 638 | --- 639 | 640 | ### 040-models-within-namespaces 641 | 642 | 643 | 644 | 645 |
Lines of Code1359
Rubycritic Score91.56
646 | 647 | The previous versions already showed the benefits of organizing the codebase. This version goes further by grouping models within namespaces. 648 | 649 | Beyond the code structure, check out the model's implementation to see how the associations reflect the namespace structure. 650 | 651 | Here is the comparison of the models' directory structure (before and after): 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 671 | 686 | 687 |
BeforeAfter
660 |
 661 | app/models
 662 | ├── account.rb
 663 | ├── application_record.rb
 664 | ├── current.rb
 665 | ├── membership.rb
 666 | ├── task_item.rb
 667 | ├── task_list.rb
 668 | ├── user.rb
 669 | └── user_token.rb
670 |
672 |
 673 | app/models
 674 | ├── account.rb
 675 | ├── application_record.rb
 676 | ├── concerns
 677 | ├── current.rb
 678 | ├── membership.rb
 679 | ├── task
 680 | │  ├── item.rb
 681 | │  └── list.rb
 682 | ├── user
 683 | │  └── token.rb
 684 | └── user.rb
685 |
688 | 689 | ### 🤔 Why this change matter? 690 | 691 | Cohesion + consistency = maintainability. 692 | 693 | ### 🔎 What the next version will have? 694 | 695 | Seven iterations have been since version `021-multi-controllers-per-entity_rest_actions_only`, but the Rubycritic score has remained the same (_**91.56**_). 696 | 697 | But what was the reason? 698 | 699 | 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. 700 | 701 | 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. 702 | 703 | `Next version`: [050-separation-of-entry-points](https://github.com/solid-process/rails-way-app/tree/050-separation-of-entry-points?tab=readme-ov-file). 704 | 705 |

⬆ back to top

706 | 707 | --- 708 | 709 | ### 050-separation-of-entry-points 710 | 711 | 712 | 713 | 714 |
Lines of Code1462
Rubycritic Score94.04
715 | 716 | 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. 717 | 718 | This separation of concerns reflects how cohesive each of these contexts has become. 719 | 720 | See how the controllers and views are now organized: 721 | 722 | **Controllers** 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 749 | 769 | 770 |
WebAPI::V1
731 |
 732 | app/controllers/web
 733 | ├── base_controller.rb
 734 | ├── task
 735 | │  ├── items
 736 | │  │  ├── base_controller.rb
 737 | │  │  ├── complete_controller.rb
 738 | │  │  └── incomplete_controller.rb
 739 | │  ├── items_controller.rb
 740 | │  └── lists_controller.rb
 741 | └── user
 742 |    ├── passwords_controller.rb
 743 |    ├── registrations_controller.rb
 744 |    ├── sessions_controller.rb
 745 |    └── settings
 746 |       ├── profiles_controller.rb
 747 |       └── tokens_controller.rb
748 |
750 |
 751 | app/controllers/api
 752 | └── v1
 753 |    ├── base_controller.rb
 754 |    ├── task
 755 |    │  ├── items
 756 |    │  │  ├── base_controller.rb
 757 |    │  │  ├── complete_controller.rb
 758 |    │  │  └── incomplete_controller.rb
 759 |    │  ├── items_controller.rb
 760 |    │  └── lists_controller.rb
 761 |    └── user
 762 |       ├── passwords
 763 |       │  └── resettings_controller.rb
 764 |       ├── passwords_controller.rb
 765 |       ├── registrations_controller.rb
 766 |       ├── sessions_controller.rb
 767 |       └── tokens_controller.rb
768 |
771 | 772 | **Views** 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 828 | 849 | 850 |
WebAPI::V1
781 |
 782 | app/views/web
 783 | ├── task
 784 | │  ├── items
 785 | │  │  ├── _form.html.erb
 786 | │  │  ├── actions
 787 | │  │  │  ├── _delete.html.erb
 788 | │  │  │  ├── _edit.html.erb
 789 | │  │  │  └── _toggle_status.html.erb
 790 | │  │  ├── edit.html.erb
 791 | │  │  ├── index.html.erb
 792 | │  │  ├── new.html.erb
 793 | │  │  └── show.html.erb
 794 | │  ├── lists
 795 | │  │  ├── _form.html.erb
 796 | │  │  ├── actions
 797 | │  │  │  ├── _delete.html.erb
 798 | │  │  │  ├── _edit.html.erb
 799 | │  │  │  └── _view_items.html.erb
 800 | │  │  ├── edit.html.erb
 801 | │  │  ├── index.html.erb
 802 | │  │  ├── new.html.erb
 803 | │  │  └── show.html.erb
 804 | │  └── shared
 805 | │     ├── _add_new.html.erb
 806 | │     └── _header.html.erb
 807 | └── user
 808 |    ├── passwords
 809 |    │  ├── edit.html.erb
 810 |    │  └── new.html.erb
 811 |    ├── registrations
 812 |    │  └── new.html.erb
 813 |    ├── sessions
 814 |    │  └── new.html.erb
 815 |    ├── settings
 816 |    │  ├── _header.html.erb
 817 |    │  ├── profiles
 818 |    │  │  └── edit.html.erb
 819 |    │  └── tokens
 820 |    │     └── edit.html.erb
 821 |    └── shared
 822 |       ├── _header.html.erb
 823 |       └── links
 824 |          ├── _reset_password.html.erb
 825 |          ├── _sign_in.html.erb
 826 |          └── _sign_up.html.erb
827 |
829 |
 830 | app/views/api
 831 | └── v1
 832 |    ├── errors
 833 |    │  ├── _response.json.jbuilder
 834 |    │  ├── from_model.json.jbuilder
 835 |    │  ├── response.json.jbuilder
 836 |    │  └── unauthorized.json.jbuilder
 837 |    ├── task
 838 |    │  ├── items
 839 |    │  │  ├── _record.json.jbuilder
 840 |    │  │  ├── index.json.jbuilder
 841 |    │  │  └── show.json.jbuilder
 842 |    │  └── lists
 843 |    │     ├── _record.json.jbuilder
 844 |    │     ├── index.json.jbuilder
 845 |    │     └── show.json.jbuilder
 846 |    └── user
 847 |       └── token.json.jbuilder
848 |
851 | 852 | #### 🤔 Why this change matter? 853 | 854 | In addition to the increased cohesion, we can also see each context has the freedom to represent and organize its resources semantically. 855 | 856 | 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`. 857 | 858 | _**This was unfeasible with the previous approach!**_ 859 | 860 | #### 🔎 What the next version will have? 861 | 862 | Apart from adding namespaces, the implementation of models has stayed the same so far. 863 | 864 | Although this version improved the Rubycritic score significantly, it introduced duplication in controllers. 865 | 866 | The next version will remove this duplication by concentrating logic in models. 867 | 868 | `Next version`: [051-separation-of-entry-points_fat-models](https://github.com/solid-process/rails-way-app/tree/051-separation-of-entry-points_fat-models?tab=readme-ov-file). 869 | 870 |

⬆ back to top

871 | 872 | --- 873 | 874 | ### 051-separation-of-entry-points_fat-models 875 | 876 | 877 | 878 | 879 |
Lines of Code1456
Rubycritic Score95.56
880 | 881 | 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. 882 | 883 | #### 🤔 Why this change matter? 884 | 885 | Because eliminating duplication generally increases maintenance. 886 | 887 | _**But be careful:**_ excessive and indiscriminate use of DRY (Don't Repeat Yourself) can compromise understanding and maintenance. 888 | 889 | Try to create abstractions only to address real needs (real problems). 890 | 891 | #### 🔎 What the next version will have? 892 | 893 | 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. 894 | 895 | `Next version`: [060-domain-model_account-member-poro](https://github.com/solid-process/rails-way-app/tree/060-domain-model_account-member-poro?tab=readme-ov-file). 896 | 897 |

⬆ back to top

898 | 899 | --- 900 | 901 | ### 060-domain-model_account-member-poro 902 | 903 | 904 | 905 | 906 |
Lines of Code1504
Rubycritic Score95.63
907 | 908 | The `Current` class had two responsibilities: containing thread-safe shared state and queries to authorize user access. 909 | 910 | 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). 911 | 912 | POROS means a model that doesn't inherit from `ActiveRecord`, and it's a common pattern to extract complex logic from `ActiveRecord` models. 913 | 914 | #### 🤔 Why this change matter? 915 | 916 | Cohesion + Separation of Concerns = Better understanding, maintainability and testability. 917 | 918 | #### 🔎 What the next version will have? 919 | 920 | 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. 921 | 922 | `Next version`: [061-domain-model_user-token-poro](https://github.com/solid-process/rails-way-app/tree/061-domain-model_user-token-poro?tab=readme-ov-file). 923 | 924 |

⬆ back to top

925 | 926 | --- 927 | 928 | ### 061-domain-model_user-token-poro 929 | 930 | 931 | 932 | 933 |
Lines of Code1519
Rubycritic Score95.68
934 | 935 | This branch introduces a **PORO** (Plain Old Ruby Objects) to handle the user token parsing, generation, and validation. 936 | 937 | 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](https://github.com/rails/rails/tree/v3.1.0)). 938 | 939 | > 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. 940 | > 941 | > 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**_. 942 | 943 | Let me emphasize this part: 944 | 945 | > Models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by 946 | the Active Model module. 947 | 948 | #### 🤔 Why this change matter? 949 | 950 | 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`. 951 | 952 | #### 🔎 What the next version will have? 953 | 954 | The next version will isolate some strings into constants to reduce codebase duplication and fragility (weak references). 955 | 956 | `Next version`: [062-domain-model_task-constants](https://github.com/solid-process/rails-way-app/tree/062-domain-model_task-constants?tab=readme-ov-file). 957 | 958 |

⬆ back to top

959 | 960 | --- 961 | 962 | ### 062-domain-model_task-constants 963 | 964 | 965 | 966 | 967 |
Lines of Code1526
Rubycritic Score95.78
968 | 969 | 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`. 970 | 971 | Note that this change also increases the Rubycritic score from `95.68` to `95.78`. 972 | 973 | #### 🤔 Why this change matter? 974 | 975 | > Coupling is good when it is stable. 976 | 977 | 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 978 | 979 | #### 🔎 What the next version will have? 980 | 981 | The next iteration will extract complex operations from the models into specialized POROs. 982 | 983 | `Next version`: [063-domain-model_user-operations](https://github.com/solid-process/rails-way-app/tree/063-domain-model_user-operations?tab=readme-ov-file). 984 | 985 |

⬆ back to top

986 | 987 | --- 988 | 989 | ### 063-domain-model_user-operations 990 | 991 | 992 | 993 | 994 | 995 |
Branch063-domain-model_user-operations
Lines of Code1563
Rubycritic Score95.77
996 | 997 | This version isolates some user operations into specialized POROs (1). The goal here is to reduce the model's complexity. 998 | 999 | Here's how the models are organized: 1000 | 1001 | ```sh 1002 | app/models 1003 | ├── user 1004 | │ ├── account_deletion.rb 1005 | │ ├── password_resetting.rb 1006 | │ ├── registration.rb 1007 | │ ├── token 1008 | │ │ └── entity.rb 1009 | │ └── token.rb 1010 | └── user.rb 1011 | ``` 1012 | 1013 | *References:* 1014 | (1) https://dev.37signals.com/vanilla-rails-is-plenty/#what-about-services 1015 | 1016 | #### 🤔 Why this change matter? 1017 | 1018 | 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. 1019 | 1020 | Beyond this, did you see that the file and folder structure reveals the domain model and what kind of operations that context can do? 1021 | 1022 | This approach can distribute the complexity over specialized classes and, as a side effect, increase the codebase's understandability. 1023 | 1024 | #### 🔎 What the next version will have? 1025 | 1026 | 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). 1027 | 1028 | `Next version`: [070-orthogonal-models](https://github.com/solid-process/rails-way-app/tree/070-orthogonal-models?tab=readme-ov-file). 1029 | 1030 |

⬆ back to top

1031 | 1032 | --- 1033 | 1034 | ### 070-orthogonal-models 1035 | 1036 | 1037 | 1038 | 1039 |
Lines of Code1613
Rubycritic Score95.81
1040 | 1041 | **Orthogonality** is the ability to change one thing without any unseen effect on other thing. 1042 | 1043 | What this branch does is to decouple the `User` from the `Account` context to make the codebase even more orthogonal. 1044 | 1045 | See how the two contexts are now separated: 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1071 | 1083 | 1084 |
AccountUser
1054 |
1055 | app/models
1056 | ├── account
1057 | │  ├── member
1058 | │  │  ├── authorization.rb
1059 | │  │  └── entity.rb
1060 | │  ├── member.rb
1061 | │  ├── membership.rb
1062 | │  ├── owner
1063 | │  │  ├── creation.rb
1064 | │  │  └── deletion.rb
1065 | │  ├── task
1066 | │  │  ├── item.rb
1067 | │  │  └── list.rb
1068 | │  └── task.rb
1069 | └── account.rb
1070 |
1072 |
1073 | app/models
1074 | ├── user
1075 | │  ├── account_deletion.rb
1076 | │  ├── password_resetting.rb
1077 | │  ├── registration.rb
1078 | │  ├── token
1079 | │  │  └── entity.rb
1080 | │  └── token.rb
1081 | └── user.rb
1082 |
1085 | 1086 | #### 🤔 Why this change matter? 1087 | 1088 | The `User` model is now more focused on the user's behavior, while the `Account` model is more focused on the account's behavior. 1089 | 1090 | 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. 1091 | 1092 | 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. 1093 | 1094 | #### 🔎 What the next version will have? 1095 | 1096 | That's all folks! There is no other branch. 😉 1097 | 1098 | 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. 1099 | 1100 | Ruby and Rails rocks! 🤘😎 1101 | 1102 |

⬆ back to top

1103 | --------------------------------------------------------------------------------