1

I have designed domain models using classes and they are the ones that get passed around. The API Request/Response model doesn't leave controller boundary, i.e. they get mapped to/from domain models either in controller or using mapper.

In general, the argument against using struct is to avoid large models, and avoid passing them around, apart from the general value type traits. With first two no longer being a concern, should I start using structs given that they might be better choice for short lived objects.

Example, API model,

//Domain Model
public class Order : Entity
{
    public DateTime Date { get; }
    public Customer Customer { get; }
    public IList<OrderItem> Items { get; }

    public Order(int id, DateTime date, Customer customer, IList<OrderItem> items)
    {
        Id = id;
        Date = date;
        Customer = customer;
        Items = items;
    }
}


//API Model
public class OrderDto //or struct
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public string CustomerName { get; set; }
    public decimal TotalAmount { get; set; }

    public static OrderDto FromDomain(Order order)
    {
        return new OrderDto
        {
            Id = order.Id,
            Date = order.Date,
            CustomerName = order.CustomerName,
            TotalAmount = order.TotalAmount
        };
    }

    public Order ToDomain()
    {
        return new Order(Id, Date, CustomerName, TotalAmount);
    }
}


public class OrderController:ControllerBase
{
   [HttpPost]
   public void CreateOrder(Order order)
   { 
      _service.AddOrder(order.ToDomainModel);
   }
}
Code Name Jack
  • 2,856
  • 24
  • 40

2 Answers2

2

With first two no longer being a concern

I would argue that claim that those are no longer a concern was not proven. First of all your sample relatively small potential Order struct is bigger than the rule of thumb size of 16 bytes. Secondary the fact that you do not explicitly pass the struct around does not mean it is not passed around by the framework itself. For example internal implementation of ASP.NET Core can pass it around between binder (which binds the incoming request to your struct) and other parts of pipeline like validation and so on and then will pass it to your controller action. Also I'm not sure that internal ASP.NET Core wirings will not result in boxing/unboxing for the struct (will check that later).

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
2

Should I use struct for API models if my domain models are different?

No.

Using a class for API models is the "standard" way of doing it. Replacing something that is usually represented by a class with a struct for performance reasons is a micro-optimization. It's something you do if all other avenues for performance optimization have be exhausted, you have done a detailed performance analysis and you have concluded that, yes, the allocation/garbage collection overhead of objects is, in fact, the only thing left to optimize, and that the performance gain it will bring offsets the cost of using a struct.

Since you are talking about "domain models" (and your example contains names such as "order number" and "payment method"), you are writing an application to support a business process. You might even use a database in your backend. If you are worried about performance, do some benchmarking/profiling and you will find tons of other areas where spending time to optimize it will yield a much higher return of investment than worrying about classes and structs.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • I have got rid of the impurities in the domain model, The original in the question was a quick and dirty copy. – Code Name Jack May 08 '23 at 16:24
  • I agree that its a micro-optimization. However if this could be something that gives benefits with less overhead, (as Dtos are by nature immutable), it might be a good quick change. Though local benchmarks are not great for insights, Will look at some benchmark tools for measurement. – Code Name Jack May 08 '23 at 16:31