[QUESTION] How to use serializers from other domains for nested relationships
While trying to implement the structure that you have posed, I have reached a point where I wonder what would be the best course of action:
In my use case, I have two domains, one that includes a User model, and another one that includes an Outfit model. I'm also using django serializers atm as they provide some helpers to ease development.
For my use case, I want to return the User model nested inside the Outfit model, but that would cross models between domains. What would be the best approach here?
from django.contrib.auth.models import User
from .models.look import Outfit
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'url']
class OutfitSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
class Meta:
model = Look
fields = ['shortcode', 'user', 'url']
This is a good question, and one my team has struggled with solving too. I briefly looked at some solutions around the internet. Things like protocol buffers try and solve this by setting up a shared resource of schemas for data. My only concern with that is: you then have the problem of controlling the distribution of that protocol buffer.
We solved this (we === the team at my current employment) by having our own UserSerializer in our domain. Because it was "our" version of the User, even though the User came from a different domain.
We finally solved it using a RelatedField that interacts with the UserInterface in the Outfits domain. This way we don't cross boundaries.
@pedrogimenez very good idea. I like it! Can you share any code examples? I can promote this issue into a question for prosperity.
I'm still not sure if this is the best approach but here's our implementation:
# com.outfits.serializers
from com.outfits.interfaces import UserInterface
class UserField(serializers.RelatedField):
def to_representation(self, value):
return UserInterface.get_user(id=value)
class OutfitSerializer(serializers.HyperlinkedModelSerializer):
user = UserField(read_only=True)
class Meta:
model = Outfit
fields = ['shortcode', 'user', 'url']
# com.outfits.interfaces
from com.users.apis import UserAPI
class UserInterface:
@staticmethod
def get_user(*, id: int) -> Dict:
return UserAPI.get(id=id)
As I said above, the OutfitSerializer sends the get_user message to the UserInterface. This way, when you need to retrieve the user serialization, you do it through the ACL.
I actually really like this pattern. I can foresee some performance issues if those interfaces are joining over network calls, but that would be a good opportunity to start keeping a shadow copy of the User in the Outfit serializer. But that only makes sense if you need a few attributes off of User. If you need the whole thing, it's a hard coupling to avoid.