2

I will give you a little context first

My company chose a design approach for our applications (windows services) to allow more code reuse through centralization (internal nuget repo) of core common code. This makes it easier to maintain test and build new services up until now..

Essentially all of the services use SqlDependency to get notified from the database that work is available for them to process. When the SqlDependency object gets notified it calls the following

public interface IRequestReceiver : IDisposable
{
    IEnumerable<IRequest> GetRequests(int maxRequestNumber);
}

which is implemented independently by each application and therefore allows each application to retrieve it's requests from it's known location and process them etc.

Now for the problem

This all worked well up until recently as all the IRequest s were very similar and so we could use a single IRequest interface in common code but put different processing logic in each. unfortunately I have been assigned the task of building a new service whose implementation of IRequest differs significantly from all the previous ones before it.

My issue is I cannot alter IRequest as it is used by multiple live services and I am obliged to stick within the design pattern of our existing services yet I have to include serveral more properties in IRequest that have not existed before and have no relevance to the existing services.

And finally my question

Given I know that an interface is a contract and should therefore not be overloaded, how would I overload or expand or even substitute the existing IRequest interface for just this new service without exposing irrelevant properties to the existing services.

The following are samples of the existing and new IRequest interface

Existing

public interface IRequest
{
    int Id { get; }

    string CreatedBy { get; }

    bool IsActive { get; set; }

    DateTimeOffset CreatedDateTime { get; set; }

    string LastUpdatedBy { get; set; }#

    DateTimeOffset LastUpdatedDateTime { get; set; }

    IRequestDetail Detail { get; }

    bool Process(CancellationToken token);
}

New

public interface IRequest
{
    int Id { get; }

    string CreatedBy { get; }

    bool IsActive { get; set; }

    DateTimeOffset CreatedDateTime { get; set; }

    string LastUpdatedBy { get; set; }#

    DateTimeOffset LastUpdatedDateTime { get; set; }

    int NotificationId { get; set; }

    int StatusId { get; set; }

    string StatusCode { get; set; }

    string StatusMessage { get; set; }

    string Destination { get; set; }

    string BusinessProcessCode { get; set; }

    string MsgType { get; set; }

    string MessageId { get; set; }

    bool Process(CancellationToken token);
}

As you can see it's just additional properties but I would not like to start introducing these to the existing IRequest interface.

Thank you

GuildBK
  • 23
  • 4
  • New interface and use it for the parts that require the new properties. For more info read here: http://stackoverflow.com/questions/8042/extension-interface-patterns – sr28 Nov 11 '16 at 11:12
  • sr28 I am not sure if this applicable as extensions are methods ?? whereas I am particularly focusing on properties that I want to extend. thanks – GuildBK Nov 11 '16 at 12:13

3 Answers3

1

Think about the open closed principle.

You have IRequest already out in production. You have clients that already use IRequest out in production too.

What you care about is the new clients( or old ones too) that would like to use the new features.

So in a nutshell. 1. Do not change anything in IRequest 2. make instead IRequest Extensions for example:

public interface IRequestExtensions
{
    int NotificationId { get; set; }

    int StatusId { get; set; }

    string StatusCode { get; set; }

    string StatusMessage { get; set; }

    string Destination { get; set; }

    string BusinessProcessCode { get; set; }

    string MsgType { get; set; }

    string MessageId { get; set; }
}

However what I see is that in the new IRequest is that you have backward incompatible types. For this explanation (since you did not mentions about incompatible types), I did not include them in the IRequestExtensions.

You can then tie up your system and provide a new interface like IRequestNew which would include both old and new things.

public interface IRequestNew : IRequest, IRequestExtensions
{
}

Now the new(and old existing) clients should use the IRequestNew (just a name, not pretty but you get the idea) and for old clients that want to implement the new interface would just add an extra, IRequestExtension to their class.

How do you migrate everyone to use IRequest as the real new name with all the features. Well, that's another story.

CJC
  • 795
  • 8
  • 25
0

Another way to solve this is to use a generic method for GetRequests

IEnumerable<T> GetRequests<T>(int maxRequestNumber);

But the class who invokes GetRequest should know what is the type they are expecting. If they Expect IRequestOld/IRequestNew, they should pass it while calling GetRequests.

IRequestOld request = GetRequest<RequestOld>(maxRequestNumber);
IRequestNew request = GetRequest<RequestNew>(maxRequestNumber);

An example program

class Program
{

    public static void Main()
    {
        Number n = new Number();
        IOne one = n.GetNumberObject<One>();
        one.PrintOne();
        ITwo two = n.GetNumberObject<Two>();
        two.PrintTwo();
    }

    public interface INumbers
    {
        T GetNumberObject<T>() where T : new();
    }
    public class Number : INumbers
    {
        public T GetNumberObject<T>() where T : new()
        {
            return new T();
        }
    }

    public interface IOne
    {
        void PrintOne();
    }
    public interface ITwo
    {
        void PrintTwo();
    }

    class One : IOne
    {
        public void PrintOne()
        {
            Console.WriteLine("One");
        }
    }
    class Two : ITwo
    {
        public void PrintTwo()
        {
            Console.WriteLine("Two");
        }
    }
}
Carbine
  • 7,849
  • 4
  • 30
  • 54
-1

Create a V2 interface that either derives from the initial version or is totally independent of it (will require quite some more work). Then use the V2 where you need it: check if the request is a V2, cast the object and then call the V2 properties.

This will probably quite some work to enable V2 properties everywhere you need it. If that is not possible, you could opt to keep two separate systems alive, running side by side.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • Could you expand on this? How would I get around the issue that V1 has items that V2 doesn't need or know how to create such as IRequestDetail? Thanks – GuildBK Nov 11 '16 at 11:15
  • You might need to put all common properties in a generic interface. Then derive from it in a V1 and V2 interface. – Patrick Hofman Nov 11 '16 at 11:16
  • ok so option 2 (running two services side by side) is not a runner (timelines and budget).. As for option 1 I could create a new version of this interface in nuget and deploy it and then hold the existing services on the old version until i got round to refactoring them into a generic with a v1 and v2 interface below it – GuildBK Nov 11 '16 at 11:25
  • had to take the option of putting all common properties in generic interface and inheriting from this. thanks fro quick response – GuildBK Nov 14 '16 at 09:54