solidusio / solidus

🛒 Solidus, the open-source eCommerce framework for industry trailblazers.
https://solidus.io
Other
4.97k stars 1.29k forks source link

Make all model classes replaceable #4139

Closed aldesantis closed 2 years ago

aldesantis commented 3 years ago

The problem

Most of the service objects used in Solidus are configurable, so that users can easily swap them with their own implementation which either inherits from or completely replaces the original. This allows users to customize the business logic in a very clean and easily testable way.

When it comes to models, things are a bit more complicated. Our official recommendation is to customize them through monkey patches, e.g.

# app/decorators/user_decorator.rb
module UserDecorator
  # Your overrides here...

  Spree::User.prepend(self)
end

This works, but it has a few drawbacks:

The solution

I would like to suggest a different solution: right now, it's already possible to override the user model class. So instead of the above decorator, I could do the following:

# app/models/user.rb
class User < Spree::User
  # Your overrides here...
end

# config/initializers/solidus.rb
Spree.user_class = 'User'

With this configuration, Solidus will use my custom User model instead of the default Spree::User. Because my model inherits from the built-in model, I still get all the out-of-the-box functionality, but I can add new features or override existing ones however I want.

This creates a clearer separation between the Solidus domain and the host app domain, it plays well with static typing and it uses class inheritance, which is a well understood pattern in all OOP languages.

Nothing prevents us from simply extending this pattern to all models. See the following example:

# app/models/order.rb
class Order < Spree::Order
  # Your overrides here...
end

# config/initializers/solidus.rb
Spree.config do |config|
  config.models.order_class = 'Order'
end

This would be a very straightforward and convenient way of replacing/extending any models with your own. It has all of the advantages of prepend with none of the drawbacks.

I'd like to gather feedback from the group and see how everyone feels about this, and whether it's worth a spike.

victorth commented 3 years ago

I think this would be very interesting and not break existing extensions or implementation that work with eval or prepend. I am wondering how you envision this for (new) extensions? They will still need to rely on monkey patching.

waiting-for-dev commented 3 years ago

That's a great idea! I just wrote something related but for the service objects: https://github.com/solidusio/solidus/pull/4138#issuecomment-890825954

We could do the same for models. We can auto-register all of them and allow users to inject the ones they need.

kennyadsl commented 3 years ago

I like the idea but I have the same concerns that @victorth raised:

I am wondering how you envision this for (new) extensions? They will still need to rely on monkey patching.