vendredi 24 février 2023

interface/inherited class in paylaod + serialize/deserialize multiple model in one property

Let me introduce the problem, we have an API that accepts multiple values in one field, let's assume we can have a request model and inside there is a field named animal like:

{
  // other properties
  "animal":
  {
    "type":"dog",
    // other specific dog properties
  }
}

or

{
  // other properties
  "animal":
  {
    "type":"cat",
    // other specific catproperties
  }
}

Now, we have layers, where we receive view model -> we transform it into dto for application layer -> and finally to db model, and vice versa for reads: db model -> dto model -> view model. We have created extension methods to map those to different layer model, eg.

internal static AnimalResponse ToDto(this AnimalReadModel dbModel)
{
  // mapping other properties
  Animal = MapAnimal(dbModel)
}

private static IAnimal MapAnimal(IAnimalDb dbAnimal)
{
  return dbAnimal switch
  {
    DogDb dog => new DogDto {//mapping dog specific props},
    CatDb cat => new CatDto {//mapping cat specific props}
  }
}

and we do that for all layers, so they look extremelly similar, but we have to multiply Cat/Dog/Other in all layers, when we want to add new animal we also need to change all those places. This seems like major solid issue we would like to solve.

And as a bonus, we have asynchronous communication between services where we send byte array, and in this solution we have used json converter (system.text.json) for another different model (but also very similar) like:

public class AnimalQueueModelConverter : JsonConverter<IAnimalQueueModel>
{
  public override IAnimalQueueModel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  {
    var jsonObject = JsonElement.ParseValue(ref reader);
    var animal = jsonObject.GetProperty("type").ToString() switch
    {
      AnimalQueueType.Dog => jsonObject.Deserialize<DogQueueModel>(),
      AnimalQueueType.Cat => jsonObject.Deserialize<CatQueueModel>()
      // if more will come we need extend this as well
    }
  }

  public override void Write(Utf8JsonWriter writer, IAnimalQueueModel value, JsonSerializerOptions options)
  {
    switch (value)
    {
      case DogQueueModel:
        // here write this model using writer
      case CatQueueModel:
        // here write this model using writer
    }
  }
}

And even tho, in the "reader" class after someone deserialize the byte[] they still need to create this stupid if cat(), if dog(), if hippo()..

So we have so many places to edit when we need to add another animal to our system. Can someone give me a pattern, lib, example what we could use to make this code better to maintain? I was thinking about disctiminators, but not sure how to propagate them. I think someone already had similar problem and there is a better solution to this.

Thanks

Aucun commentaire:

Enregistrer un commentaire