Aside: Even though Rails has an inbuilt mechanism to serialize domain objects, ActiveModel Serializers (AMS) is a popular library for decoupling the serialization logic from database layer. This post assumes usage of AMS.
Change is the only constant in software development and it is the view or presentation layer that changes the most. In case of API-only apps, JSON schema is the view.
Assume we have resources like
inventory with corresponding serializers.
Now product info is also required in inventory details.
would pick the main product serializer which would render the entire product details which we don't need.
So we create another serializer
InventoryProductSerializer because adding some method with hand-rolled json is not a good practice - which is exactly why we use serializers in the first place.
Then we also have
delivery_order - which also needs product info but with slightly different fields.
We think of saving those precious bytes on the network by sending only required fields and not single more. So we religiously create
This goes on and on...
product_serializer.rb inventory_product_serializer.rb delivery_order_product_serializer.rb
Lets take a moment and see if we can reduce this. Ideally
inventory-product serializer will be used
So why not somehow put it inside
InventorySerializer where it belongs?
We can conveniently take advantage of Ruby's
constant lookup algorithm to do this:
class InventorySerializer < ActiveModel::Serializer attributes :id, :location, :quantity has_one :product # Return only required attributes here to keep API small class ProductSerializer < ActiveModel::Serializer attributes :id, :name, :price end end
class DeliveryOrderSerializer < ActiveModel::Serializer attributes :id, :quantity, :price, :source, :destination has_one :product # Return only required attributes here to keep API small class ProductSerializer < ActiveModel::Serializer attributes :id, :name end end
While looking up for any constant (here the class constant
ProductSerializer), it is first looked up inside the serializer class itself.
We have one there and which gets used. The next lookup of outer global
product_serializer.rb never happens in this case.
Thus we have achieved colocated concerns. Hope this helps.