0

My application has to maintain a stateful object and update it periodically based on events received. I get an initial HTTP request to create this stateful object. After that the object has to be updated asynchronously(using RabbitMQ in my case).

On my initial HTTP request I create a domain object. I thought I'd just cache the entire domain object and just use it like a stateful object. I use the IDistributedCache interface to cache this domain object. In my implementation of IDistributedCache I use System.Text.Json to deserialize/serialize the value to be cached.

I get the following error when deserializing. My domain's public constructor is the one I use to create the domain object. But some of the properties of my domain class is not required to create it. I end up with the following error.

Each parameter in the deserialization constructor on type 'DomainObject' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. Fields are only considered when 'JsonSerializerOptions.IncludeFields' is enabled. The match can be case-insensitive.

I could create a constructor and mark it with [JsonConstructor] attribute but wouldn't that violate DDD principles? The JsonConstructor can be used to create a domain object.

Should I create a separate class to be used as a cache object in my case? Is that the best approach?

Edit: Code Sample My Domain classes,

public class Player : Entity
{
    public string Name { get; private set; } = string.Empty;
    public int Age { get; private set; }

    private readonly IEnumerable<Item> _items = Enumerable.Empty<Item>();

    public IReadOnlyCollection<Item> Items => _items.ToList();

    public Player(string name, int age, IEnumerable<Item> items)
    {
        Name = name;
        Age = age;
    }
}

public class Item : Entity
{
    public string Name { get; private set; } = string.Empty;
    public string Value { get; private set; }

    public Item(string name, string value)
    {
        Name = name;
        Value = value;
    }
}
public abstract class Entity
{
    int? _requestedHashCode;
    int _Id;
    public virtual int Id
    {
        get
        {
            return _Id;
        }
        protected set
        {
            _Id = value;
        }
    }

    public bool IsTransient()
    {
        return this.Id == default(Int32);
    }

If I use the Player domain object as a stateful cache object, I face issues while deserializing it.

    var items = new List<Item>() { new Item("x", "abcd"), new Item("y", "efgh") };

var player = new Player("mario", 21, items);

string jsonPlayer = JsonSerializer.Serialize(player);

Player deserializedPlayer = JsonSerializer.Deserialize<Player>(jsonPlayer);
7
  • I miss a code snippet demonstrating your problem.
    – Doc Brown
    Commented Aug 9, 2023 at 16:57
  • 1
    which particular DDD principle are you violating?
    – Ewan
    Commented Aug 9, 2023 at 17:50
  • @Ewan the domain model should solve business problems and should (ideally) not be concerned with technical details related to storing its state or serialisation.
    – Rik D
    Commented Aug 9, 2023 at 18:14
  • @DocBrown Added snippet. Commented Aug 9, 2023 at 18:35
  • @user1890098 not trying to be rude, but the code you posted has little to do with DDD. It suffers from primitive obsession and it has no behavior whatsoever.
    – Rik D
    Commented Aug 9, 2023 at 18:38

2 Answers 2

1

Attribute Orientated Programming, is known for its propensity to muck up dependency diagrams. But usually there is an alternate way of defining the mapping properties.

For example, Newtonsoft Json provides custom type de/serialisers

https://stackoverflow.com/questions/23017716/json-net-how-to-deserialize-without-using-the-default-constructor

I think most people have abandoned attributes in favor of the code setup for exactly this reason. They don't want to couple their classes to the datalayer.

For your specific problem, as much as i can glean from your code I would recommend one of the following.

  1. Your classes look overcomplicated. switch to structs and put the logic in services, or make all the properties public get, set. an ADM approach.

    This would be my general approach for distributed systems, but its not good if you have a lot of in memory operations and deep objects. With Player and Item I guess you are "saving a game" so this might not work for you.

  2. Use the flyweight pattern or a DTO version of your object to persist the data rather than serialising the object itself.

    I think this is going to be equivalent to writing the custom serialisation, but it might be better if you can do some optimisations around memory use

  3. Abandon JSON and the library in favor of custom serialisation.

    If you have a large object this might be better, you can save space and improve the serialisation/deserialisation performance

  4. Write the custom type serialiser

    Its not that hard and it solves your immediate dilemma. There should be a way to just manually say "use this constructor for this object" you wont have to manually fiddle with all the fields

3
  • I've added code sample now. It's going to be a hassle to map base class fields/properties as well. Is it better to create a new object and use it exclusively as a caching object? The problem is my object to be cached also has business logic which is part of my domain. Commented Aug 9, 2023 at 18:38
  • I'll edit the answer and give you some options
    – Ewan
    Commented Aug 9, 2023 at 20:01
  • 1
    Thanks for the help. I’ve not tried any as we’ve gone in other way for caching which wouldn’t cause these issues. Commented Aug 10, 2023 at 22:39
1

Microsoft addresses this question directly:

"...read-only enough to be practical." practical is good.

It could be argued that value objects, being immutable, should be read-only (that is, have get-only properties), and that's indeed true. However, value objects are usually serialized and deserialized to go through message queues, and being read-only stops the deserializer from assigning values, so you just leave them as private set, which is read-only enough to be practical.

the sample implementation (edited to isolate OP's question):

public class Address 
{
    public string Street {get; private set;}
    public string City {get; private set;}
    ...
    public Address() { }

    public Address(string street, string city, ...)
    {
        Street = street;
        City = city;
        ...
    }

here is the link

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.