[LRUG] Objects that are neither classes nor records but something in between

Matt Spendlove matt at cenatus.org
Wed Jun 23 06:17:07 PDT 2021


Hey Tim

I’m curious about the motivation for modelling these structures as POROs in
the first place. It’s not clear to me what use they have without some kinda
persistence?

Identifying the boundaries of collaborating entities is always a bit
contentious but for straightforward projects, the multifaceted Rails style
model approach is perfectly functional imho. I also quite like the
repository style abstraction used in Elixir/Ecto for example.

I can see the appeal of building out simple entity structures and only
adding what you need when required but I wonder, if you just took the
“sensible defaults” of a framework like Rails, would this still be
confusing?

I guess you could still wrap up data access and separate business logic
functions into some collaborating Module or Class if you really wanna keep
things separate.

✌️


On Tue, 22 Jun 2021 at 17:05, Andrew Donovan <andrew_donovan at hotmail.com>
wrote:

> Hi Tim,
>
> Your db or permanent storage is your source of truth , if you’re using
> Rails then use all the facilities it offers such as ActiveRecord. If
> Product is somehow defining prices and other behaviour for your digital
> items then it needs to be modelled in the db. If you don’t have a db schema
> I’d draw out a simply entity relationship diagram which should help in
> identifying where things are missing or don’t tie up. From the gist of the
> details below it sounds like you have access to a database. I’d be wary of
> using code for defining product strategy, amongst other things how do you
> record product changes over time ?
>
> Regards
>
> Andrew
>
>
>
>
>
>
>
> Regards
>
> Andrew
>
>
>
> *From:* Chat <chat-bounces at lists.lrug.org> *On Behalf Of *Tim Cowlishaw
> *Sent:* 22 June 2021 15:43
> *To:* Ruby Group <chat at lists.lrug.org>
> *Subject:* [LRUG] Objects that are neither classes nor records b.ut
> something in between
>
>
>
> Hi there folks! Hope you're all doing well.
>
>
>
> I've just run into a problem / conundrum / anti-pattern / code smell that
> seems to recur fairly frequently in projects I've worked on of late, and
> which i'm not really sure how to name in order to search for an existing
> solution, so i figured I'd post it here in case anyone has some sensible
> advice, or in the hope that it provokes some interesting discussion, at
> least.
>
>
>
> The problem basically arises when there's some 'object' in my system
> (using this word in the vaguest possible sense for now) which has both
> *data* (so fields that can be updated through an API or webapp, and which
> should probably be stored in a database row), but also has a one-to-one
> correspondence with a specific bit of behaviour, expressed as code.
>
>
>
> An example:
>
>
>
> I'm currently working on  a project which is a digital library. It
> contains lots of Books:
>
>
>
> class Book < Struct.new(:id, :title, :author, :content); end
>
>
>
> Users can buy access to these books either individually (all books are the
> same price), or by buying a subscription to the whole library. Forgetting
> the DB / ORM side of this for a sec, i'd model a toy implementation of what
> i want to do something like the following:
>
>
>
> class User
>
>   def initialize(purchases=[])
>
>     @purchases = purchases
>
>    end
>
>    attr_reader :purchases
>
>
>
>    def has_access_to?(book)
>
>      purchases.any? { |p| p.grants_access_to?(book) }
>
>    end
>
> end
>
>
>
> class Purchase < Struct.new(:product)
>
>   def grants_access_to?(book)
>
>     product.grants_access_to?(book)
>
>   end
>
> end
>
>
>
> class Product
>
>   def self.price
>
>    raise NotImplementedError
>
>   end
>
>
>
>   def self.name
>
>     raise NotImplementedError
>
>   end
>
>
>
>   def grants_access_to?(book)
>
>     raise NotImplementedError
>
>   end
>
> end
>
>
>
> class OneOffPurchase < Purchase
>
>   def initialize(book)
>
>     @book = book
>
>   end
>
>   attr_reader :book
>
>
>
>   def self.price
>
>     500
>
>   end
>
>
>
>   def self.name
>
>     "A single book"
>
>   end
>
>
>
>   def grants_access_to?(other_book)
>
>     book.id == other_book.id
>
>   end
>
> end
>
>
>
> def Subscription < Purchase
>
>   def self.price
>
>     500
>
>   end
>
>
>
>   def self.name
>
>     "All inclusive special subscription"
>
>   end
>
>
>
>   def grants_access_to?(book)
>
>     true
>
>   end
>
> end
>
>
>
> This all to me looks reasonably sensible so far, but i also want those
> product names and prices to be editable by site admins through our
> 'backoffice' webapp, so they need to be stored in the DB, and this is where
> I have trouble finding a solution i'm happy with. Naively I could have an
> ActiveRecord type object and a fairly simple dispatch mechanism to the code
> that works out the permissions, but this has some fairly obvious flaws:
>
>
>
> class Product < Struct.new(:unique_key, :name, :price, :book)
>
>   def grants_accesss_to?(other_book)
>
>      if unique_key = :one_off && book.present?
>
>        book.id == other_book.id
>
>    elsif unique_key == :one_off
>
>       raise "oops! we're in a totally inconsistent state
>
>     elsif unique_key == :subscription && book.present?
>
>        raise "yep, this makes absolutely no sense either"
>
>     elsif unique_key == :subscription
>
>       true
>
>    else
>
>      raise "this isn't even a real product type. hopeless."
>
>     end
>
> end
>
>
>
> aside from all the brittleness and smells that you can see above, this is
> also kinda useless in a bunch of respects - we can't add new product types
> without making a change to the codebase, we have to do a bunch of
> convoluted validation to avoid all the various possible inconsistent states
> we can get into above, and our codebase is tightly coupled to the value of
> that unique_key database field, which i'm very suspicious of?
>
>
>
> Therefore, anyone got any smart ideas about how to do this better? The
> requirements I'm looking to fulfil, in summary:
>
>
>
> 1) A  product has a specific 'strategy' for granting access to a book for
> a user, expressed as  ruby code
>
> 2) A product has a price and name that is editable as data through our web
> app's admin interface
>
>
>
> And, in general, i'd be interested to know -  is there a name for this
> type of (anti-)pattern - Types of things that refuse to sensibly sit in the
> domain of software classes or Database fields? Does any of this even make
> any sense? I'm not so sure myself anymore.
>
>
>
> Any thoughts gratefully received!
>
>
>
> Cheers,
>
>
>
> Tim
>
>
> _______________________________________________
> Chat mailing list
> Chat at lists.lrug.org
> Archives: http://lists.lrug.org/pipermail/chat-lrug.org
> Manage your subscription: http://lists.lrug.org/options.cgi/chat-lrug.org
> List info: http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>
-- 
Sent from Gmail Mobile
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lrug.org/pipermail/chat-lrug.org/attachments/20210623/71d12aee/attachment.html>


More information about the Chat mailing list