2

I have a client (Android app), and a server (WebApi REST web-service).

The app has a service layer it calls to communicate with the web-service for reading and writing records. Return values are sent back from the web-service's controllers (ie, when a new record is written to the DB and the associated record-id is generated).

I have many different controllers, each having multiple methods that all return a different number and type of values (they mostly return int array's, but occasionally I return an object array if there non-integer values mixed in).

The Service layer on the app side does a few checks to validate the parameters, such a checking the argument count (array elements) and checking for pre-defined invalid/uninitialized values (like -9999).

I wan't to formalize these parameter operations by defining an interface so that things like the count and type of the parameters used on both sides are explicitly specified in one location (so I'm not "winging it" on both sides).

However, the dilemma I'm running into is deciding on whether to define one interface that specifies both the client and server related methods, or to define two separate interfaces, each specifying methods specific that side of communication.

If I have a single interface, like so:

public interface IServiceParams
{
   object[] CreateParams(int recordId, string recordNumber);

   bool ParseParams(object[] paramz, out int recordId, out string recordNumber);
}

then since CreateParams is only used on the WS side, and ParseParams on the App side, I would have to declare empty implementations on either side

Web Service Side:

public class RecordController : ApiController, IServiceParams
{
   object[] IServiceParams.CreateParams(int recordId, string recordNumber)
   {
      return new[] { newRecordId, newRecordNumber };
   }

   bool IServiceParams.ParseParams(object[] paramz, out int recordId, out string recordNumber)
   {
      throw new NotImpementedException();
   }

   ////////////////////////////////////////////////////////////////////////////////////////////

   public object[] Put( Record record )
   {
      if( !DbLayer.Update(record) )
      {
         return null;
      }

      return CreateParams(record.RecordId, record.RecordNumber);
   }
}

App Side:

public class RecordService : IServiceParams
{
   object[] IServiceParams.CreateParams(int recordId, string recordNumber)
   {
      throw new NotImpementedException();
   }

   bool IServiceParams.ParseParams(object[] paramz, out string recordNumber)
   {
      if( paramz == null || paramz.Count != 4 )
      {
         return false;
      }

      if(!ParseInt(paramz[0].ToString(), out recordId) || recordIdNew == DEFAULT_RECORD_ID)
      {
         return false;
      }

      // ...

      return true;
   }

   ////////////////////////////////////////////////////////////////////////////////////////////

   public bool UpdateRecord( Record record )
   {
      object[] paramz = WebService.Put( record );

      int recordId; 
      string recordNumber;

      if(!ParseParams(paramz, out recordId, out recordNumber))
      { 
         return false;
      }

      record.RecordId = recordId;
      record.RecordNumber = recordNumber;

      return true;
   }
}    

Or, should I define two interfaces, and implement each on their respective sides, thus eliminating the need to have empty implementations:

public interface IServiceParamsClient
{
   bool ParseParams(object[] paramz, out int recordId, out string recordNumber );
}

public interface IServiceParamsServer
{
   object[] CreateParams(int recordId, string recordNumber);
}

Thanks a lot for the help!

samus
  • 6,102
  • 6
  • 31
  • 69
  • Currently where is the `IServiceParams` declared? – nawfal Oct 22 '13 at 17:07
  • I declared IServiceParams on the app side b/c the web-service is already sharing the model code from the app (so it knows the record types when reading and writing them). – samus Oct 22 '13 at 17:11
  • 1
    Ok, in that case go ahead with two interface model (that's not even a question). The only improvement I can suggest is wrapping the params into one strongly typed class. It's much safer that way. Not sure if I should make it an answer. – nawfal Oct 22 '13 at 17:12
  • ...which is a good thing? What are you losing by splitting into two projects? Or you can even have them at App side since you can access them from Web side right? – nawfal Oct 22 '13 at 17:16
  • Well, I appreciate your suggestion, and is the reason why I go to all the trouble of posting exactly what is going on, for these other tips. If you want to add it as an answer I'll certainly upvote it, and others may in the future... can hurt :) – samus Oct 22 '13 at 17:16
  • @nawfal The web-service is in a separate project than the app b/c I didn't want to try and cram everything into on big project, and I was new to a lot of this technology (Android, WebApi) a year and a half ago when I started. – samus Oct 22 '13 at 17:19
  • Sam, nothing wrong with it. If you think you can cram a project, you will fail :) Certainly they can hold a lot! :) What you should base your design is on where data/logic logically falls. Also I feel you're not doing it right by having Web service having access to your App side. – nawfal Oct 22 '13 at 17:22
  • @nawfal ... oh I see now why you suggested to wrap the params into a type, this will solve the new issue I just realized when splitting the interfaces: since the interfaces are independent, there is no single place of specification for the the parameters, and so the method signatures could end up being changed independently, therefore breaking things. If both interface methods use this new type, it will serve as the glue that binds them together, ensuring consistency (though I suppose a the type used can now change, but this is much more unlikely). – samus Oct 22 '13 at 17:27
  • Sam, exactly, otherwise you're duplicating info! – nawfal Oct 22 '13 at 17:28
  • 1
    @nawfal ... Yeah, there are many things I'm doing wrong for sure. You know, I should house the models in the WS project, and then have the App project point to them, since the models are based on the DB tables on the WS side of things! That wouldn't be too much work, just need to put in on "the list" :) – samus Oct 22 '13 at 17:32
  • @nawfal If you mention everything you suggested, both the splitting of the interfaces, and then throw in the new data-type suggestion, I can then accept that as an answer. I mean, you did suggest to me exactly what to do based on my feedback, even if its similar to any other answers. – samus Oct 22 '13 at 17:34

2 Answers2

2

Is there any reason you'd need both methods to be declared in one interface? If not, make two separate interfaces that each reflect their intent. Having to implement a method as "not supported" is a design smell. See: What's the right way to design my interface when I have operations that aren't supported by all implementers?

Community
  • 1
  • 1
Zymurgeek
  • 169
  • 7
  • No, there is no reason. This interface is being created now, specifically for dealing with the web-service's return parameters. Thanks for the link! – samus Oct 22 '13 at 16:53
  • The composition method (#1) proposed in the solution is what I was thinking of, but really wasn't sure if it was a bad idea to have an inheritance tree of small interfaces... just seemed overly complex. Ahh well. – samus Oct 22 '13 at 16:58
1

Go for two interface design. By doing so, you're doing yourself a favour with a cleaner API. The one suggestion is you can encapsulate the parameter logic in one class, so that now you have a stronger types statically checked which is not a small thing. Secondly I'm not sure if Web service coming to know of App client is a good thing. I think it should be the other way around. So here is how I would do it:

public class Param //at server side
{
   public int recordId { get; set; }
   public int transToPropId { get; set; }
   public int receivedViaTypeId { get; set; }
   public string recordNumber { get; set; }
}

public interface IParamParser //at client
{
   //I do not know what object[] paramz is; I leave that to you; 
   //may be it needs encapsulation as well
   bool Parse(object[] paramz, Param p);
}

public interface IParamCreator //at server
{
   Param CreateParams(int recordId, int transToPropId, int receivedViaTypeId, 
                      string recordNumber);
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • 1
    Using a wrapper type around my parameters has eliminated any (current) need for interfaces. For CreateParams, I just use a Param's constructor, and no parsing is needed since the serializer will take care of that (I could use a validation method I guess). – samus Oct 22 '13 at 18:25
  • When defining 2 distinct interfaces, is the expectation that any implementation of one interface is to always work with any implementation of the other interface? It would seem like it should, being that we used interfaces to begin with. However, it seems that this could prevent optimized transfers rates by being forced to keep things generic. If, instead, there was a single interface with both sides of the connection defined, then how data is transferred between the client and the server could be optimized. That's the part I'm personally struggling with. – ThermoX Nov 25 '17 at 23:03