Skip to content
This repository has been archived by the owner on Feb 22, 2024. It is now read-only.

Service Objects vs Service Mixins #39

Closed
yiumingh opened this issue Jun 3, 2015 · 4 comments
Closed

Service Objects vs Service Mixins #39

yiumingh opened this issue Jun 3, 2015 · 4 comments

Comments

@yiumingh
Copy link

yiumingh commented Jun 3, 2015

I'm wondering what are the drawbacks of using mixins vs using objects. I actually implemented services by using a mixin instead of a separate object on its own.

This helps me in a two ways:

  1. I don't have to define model, since I'm using classmethods to obtain the model I'm running the query on.
  2. I don't have to create or instantiate objects to use getters like all, find, etc.
class ServiceMixin(object):

    def save(self):
        db.session.add(self)
        db.session.commit()
        return self

    @classmethod
    def all(cls):
        return cls.query.all()

    @classmethod
    def get(cls, _id):
        return cls.query.get(_id)

    @classmethod
    def get_all(cls, id_cl="id", *ids):
        id_column = cls.get(id_cl)
        return cls.query.filter(id_column.in_(ids)).all()

    @classmethod
    def find(cls, **kwargs):
        return cls.query.filter_by(**kwargs)

    @classmethod
    def first(cls, **kwargs):
        return cls.find(**kwargs).first()

    @classmethod
    def get_or_404(cls, id):
        return cls.query.get_or_404(id)

    @classmethod
    def new(cls, **kwargs):
        return cls(**cls._preprocess_params(kwargs))

    @staticmethod
    def _preprocess_params(kwargs):
        kwargs.pop('csrf_token', None)
        return kwargs

    @classmethod
    def create(cls, **kwargs):
        return cls.save(cls.new(**kwargs))

    def update(self, **kwargs):
        for k, v in self._preprocess_params(kwargs).items():
            setattr(self, k, v)
        self.save()
        return self

    def delete(self):
        db.session.delete(self)
        db.session.commit()

To use it, simply inherit it when creating your models:

class Product(db.Model, ServiceMixin):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(250))
    ...

Product.all() # Gets all of the products
Product.find(name="tacos") # Finds all products named "tacos"
p = Product(name="spegahetti")
p.save() # Saves the newly created Product

Anyway, this project template is great stuff! Thanks for letting us in on this.

@Bekt
Copy link

Bekt commented Jun 4, 2015

You are right. I use a similar pattern in my code except I only have get, save, and delete operations.

@mattupstate
Copy link
Owner

It's a matter of design, which we can argue all day if you want 😏, and thats because design is a matter of opinion. If that works for you, thats fine. I, however, don't like to mix responsibilities in my objects. For example, I don't believe this mixin approach is appropriate when you begin to have more complicated business logic when instances of your models are created, updated, or deleted. A hypothetical scenario might be that, when you create a Product record you must also publish a message to a message queue. In my opinion, it would be inappropriate to encapsulate that logic in a class method on the Product model class. More appropriate would be an object that brings together separate functions that deal with both operations, making the logic easier to test.

@yiumingh
Copy link
Author

yiumingh commented Jun 4, 2015

Come to think of it, I agree with you on that part. Using service models let's you draw the line between disparate systems.

So just one final question, why not make the methods in the service object static?

EDIT: I initially opened this topic to obtain clarification on why certain design decisions were made in the project template. After given that example, I got a better understanding why it was designed the way it was.

Anyway, I was trying to get away from instantiating the service (especially since I didn't need multiple service objects with different configurations). The service object will definitely need to be instantiated if you want to create differently configured services, and that's why this exercise is pretty much a futile one.

Anyway, thanks a bunch! I had a small epiphany moment when thinking about this.

@yiumingh yiumingh closed this as completed Jun 5, 2015
@mattupstate
Copy link
Owner

Awesome! Good luck in your work.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants