24

MassTransit states that we should use interfaces for message contracts :

It is strongly suggested to use interfaces for message contracts, based on experience over several years with varying levels of developer experience. MassTransit will create dynamic interface implementations for the messages, ensuring a clean separation of the message contract from the consumer.

Source : Docs » Using MassTransit » Creating a message contract

What is downside of using POCO DTOs instead of interfaces, what are the obvious advantages of Interfaces over classes in the context of messaging with MassTransit?

On the other hand NServiceBus is fine with POCO, and sure it makes sense to not use internal classes like domain object, see below.

Source : NServiceBus » Messaging » Messages, Events and Commands

When creating messages one should follow the following guidelines:

  • Messages should be simple POCO objects.
  • Messages should be as small as possible.
  • Messages should satisfy a Single Responsibility Principle.
  • Classes used for other purposes (e.g. domain objects, data access objects and UI binding objects) should not be used as messages.
Edgars Pivovarenoks
  • 1,526
  • 1
  • 17
  • 31
  • Pretty simple what the advantages are: ensuring a clean separation of the message contract from the consumer – Jamie Rees Jun 05 '17 at 12:11
  • 1
    Wouldn't POCO be like 2nd best? I mean the wording - "strongly suggested" implies some sort of risk or negative consequences of not using interfaces, which I fail to see at this moment. – Edgars Pivovarenoks Jun 05 '17 at 12:19
  • 2
    The _Interface_ would just be a layer of separation to allow changes to the POCO methods to only have to be migrated ton one block of code. There could be dozens of different _save_ commands throughout the code that would need to be edited on a simple table change, but if those commands just funnel to a _Save_ interface you would only need to edit the 1 instance – Mad Myche Jun 05 '17 at 12:33
  • 8
    It's guidance, to ensure that behavior doesn't creep into message contracts. POCO usage is certainly allowed, most teams porting from NSB don't change their message assemblies and they work just fine. – Chris Patterson Jun 05 '17 at 13:13
  • 5
    @ChrisPatterson that is no longer the case with the next C# where interfaces can contain functionality :-) – Carlo V. Dango Nov 22 '17 at 10:03

3 Answers3

1

Although not directly related to MassTransit, but I would say the reason is encapsulation. Creating a simple POCO with setters, but passing it on behind interface that promises only getters encapsulates the data in the DTO.

public class Program
{
    static void Main(string[] args)
    {
        User user = new User()
        {
            Username = "user99"
        };

        Account account = new Account(user);
    }
}

public interface IUser
{
    string Username { get; }
}

public class User : IUser
{
    public string Username { get; set; }
}

public class Account
{
    private IUser user;

    //Currently used in main
    public Account(User user)
    {
        user.Username = ""; //This is allowed            
        this.user = user;
    }

    //But consider this ctor
    public Account(IUser user)
    {
        //user.Username = ""; //Not allowed
        this.user = user;
    }

    ...
}
Siim Haas
  • 485
  • 2
  • 6
  • 15
  • I get it, but this is not full picture - as you need to implement User on client side aka RemoteUser : IUser {}, then deserialize it, now it is still your responsibility to use the interface in your client code and not mutate your own implementation. – Edgars Pivovarenoks Jun 06 '17 at 09:52
  • 3
    With POCO aproach you can use immutable classes with constructor parameters and get same guarantees. JSON.NET supports deserialization through constructor parameters, so pretty easy. And you reduce the burden of implementation on client side. – Edgars Pivovarenoks Jun 06 '17 at 09:55
0

Using interfaces can make it easier to work with multiple versions of a message. You can have a two separate interfaces, one for each version, and a POCO object implementing two interfaces.

There is a clear definitition of a contract while retaining the compatibility with older version of a contract.

Use the new interface on the output - the code clearly defines the contract.

Use POCO on the input - keep compatibility with both versions.

jahav
  • 699
  • 1
  • 7
  • 24
-1

I think easier dependency injection and multiple implementations as needed providing unified msgs form across the project but different handling logic as much as required

long story short cleaner code and higher maintainability

Massaynus
  • 322
  • 2
  • 9