Rails: Example on how to extract domain independent code from the `app/models` folder to the `lib/` folder

Posted . Visible to the public.

This cards describes an example with a Github Client on how to keep your Rails application more maintainable by extracting domain independent code from the app/models folder to the lib/ folder. The approach is applicable to arbitrary scenarios and not limited to API clients.

Example

Let's say we have a Rails application that synchronizes its users with the Github API:

.
└── app
    └── models
        ├── user
        │   ├── github_client.rb
        │   └── sychronizer.rb
        └── user.rb

In this example the app folder contains domain dependent code (user.rb and sychronizer.rb) and domain independent code (github_client.rb).

Potential Refactoring

While the Rails guide Show archive.org snapshot is very unspecific to the usage of the lib folder (it says "Extended modules for your application"), we set up some kind of rules for the github_client.rb which code is allowed to live in the lib folder:

  • There should be some namespace in the lib folder e.g. lib/github_client, which isolates this code from other logical units.
  • There should be a strict interface between the code in e.g. app/models/ and the lib/github_client. You can think of it as packaging this code into a gem, allowing various projects from different domains to work with it equally well.

Following these rules the structure could look similar like this:

.
├── app
│   └── models
│       ├── user
│       │   └── sychronizer.rb
│       └── user.rb
├── config
│   └── initializers
│       └── github_client.rb
└── lib
    └── github_client
        ├── client.rb
        └── configuration.rb

With this approach you have a stronger decoupling in your application:

  • Your unit tests might use Webmock or VCR for the lib/github_client part, but your application only talks to stubs or adapters and never know anything about the API internals.
  • It feels more natural, that you don't call client internals from your models. Only the public methods like GithubClient::Client.new.users('makandra GmbH') or GithubClient::Client.configure { ... } are used in your model context.

Additional notes

Last edit
Emanuel
License
Source code in this card is licensed under the MIT License.
Posted by Emanuel to makandra dev (2024-07-31 09:18)