What is the best practice to define method signatu

2019-05-06 15:51发布

问题:

What is the best practice to define service call prototype/signature while developing application in service oriented architecture.

For Example I want to create Service call to send email.

Let say I have following object in my domain layer

 [datacontract]
public class Email
{
      public string To { get; set; }
      public string From { get; set; }
      public string Message { get; set; }
      public string Subject { get; set; }

      //I am not going to use this properties in send email method
      public string OtherProp1 {get; set;}
      public string OtherProp2 {get; set;}
      public string OtherProp3 {get; set;}

  }

Should I create Service method signature like

   public bool SendEmail(string from,string to, string subject,string message){//My Logic}}

Or

   public bool SendEmail(Email myEmail){//My Logic}

I am leaning toward first option.(Having said that I know if object is way to complex than it make sense to pass whole object itself.)

回答1:

Unfortunately, second option is less clear in this case and this is because of your Email class.

Suppose I am to call your method. You make me pass an instance of the Email class. I go to the class contract and find 7 properties.

And, how am I supposed to know which parameters are mandatory and which are optional? From the docs? Not a cleanest design if I have to consult the docs to make proper use of the API.

I would rather refactor your Email class to call it EmailRequest with all these optional parameters removed and then I would create yet another class EmailResponse if you ever need to use it as a return value of a service.



回答2:

I too vote for approach #2.

Since you mentioned 'service oriented architecture', you should create a DataContract to gain full control of what your clients see.

Here, serialization is opt-in, so the 'unused properties' will not be sent over the wire.

Plus, you get other benefits like controlling the order of serialized members, specifying if fields are required or not, custom names, versioning and so on. It just makes things obvious for the clients.

Also, the DataContractSerializer is supposedly 10% faster than the XmlSerializer. More details on this blog, though I am not sure if approach #1 (primitive types) would use XmlSerializer or DataContractSerializer.

[DataContract(Name="MyEmail", Namespace="http://contoso.org/soa/datacontracts")]
public class Email
{
  [DataMember(Name="ToField", IsRequired=true, Order=0]
  public string To { get; set; }

  [DataMember(Name="FromField", IsRequired=false, Order=1]
  public string From { get; set; }

  [DataMember(Name="MessageField", IsRequired=true, Order=2]
  public string Message { get; set; }

  [DataMember(Name="SubjectField", IsRequired=false, Order=3]
  public string Subject { get; set; }

  public string OtherProp1 {get; set;}
  public string OtherProp2 {get; set;}
  public string OtherProp3 {get; set;}    
}


回答3:

It's always better to go OOP way. If email class is excessive, try to analyze and define another solution. Like this

   public class Email
    {
          //everything needed for service is extracted in another class
          public EmailAddressDetails {get;set}
          //I am not going to use this properties in send email method
          public string OtherProp1 {get; set;}
          public string OtherProp2 {get; set;}
          public string OtherProp3 {get; set;}

      }

and use service like this

   public bool SendEmail(EmailAddressDetails email){//My Logic}}


回答4:

How about writing both? And simply having the first, call the second?



回答5:

I would make your Email object a [DataContract] and go with option two. It's my opinion that you don't want that many parameters for one service method.



回答6:

One of the important aspects of SOA is the contract so I would really vote against cluttering it with unneeded data and details which will cause it to deteriorate fast(er).

The option offered by channs is pretty good as it concentrates on defining the contract explicitly and in details, but I'd also separate the classes used for contract purposes from the classes used internally to decouple and hide internal implementation details (I explain this in detail in the Edge Component pattern)