3

I work for a company that has multiple websites, and the existing infrastructure is...well, awful.

Right now, each store has its own table that varies in structure. This is rapidly becoming a problem (if it wasn't already).

So I need to find a way to take orders from multiple channels, format them into a single, unified way, store them in our end, and translate them back to the format expected by the store APIs (which can be everything from RESTful JSON to SOAP).

My initial gut-reaction was a mixture of factory, visitor, and builder patterns (plus some smart polymorphism), but it got a little bit too complex too quickly. Before I go and code myself into a corner with a solution that may not be optimal, maintainable or extensible, is there a pattern or set of patterns that would be more efficient?

Basically, I'm thinking it would be something like:

Source -> Translator -> Our Format
Our Format -> Translator -> Source

I don't need the translator to actually act on the data. All it should be responsible for is getting it in the right format, and we can get it from point A to point B (and vice versa).

A few assumptions about the system:

  1. The format on our end is unlikely to change, and therefore our objects are unlikely to change
  2. When the data is translated back to the original source, we can assume that everything that's required will be there. The actual behavior for out-bound requests is small, focused and well-defined

3 Answers3

3

Adapter Pattern is your friend. Let's say you have a legacy Customer class.

public class CustomerLegacy
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime Birthday { get; set; }
}

The first thing you might want to do is to extract of that class. This step is optional but it makes the new class testable. So, you will have ICustomerLegacy interface that looks like below.

public interface ICustomerLegacy
{
    string FirstName { get; set; }

    string LastName { get; set; }

    DateTime Birthday { get; set; }
}

Then refactor CustomerLegacy class to implement the new interface.

public class CustomerLegacy
    : ICustomerLegacy

The next step is to create an adapter that takes ICustomerLegacy as a constructor's argument. You can add new properties to serve your needs.

public class CustomerAdapter
{
    private readonly ICustomerLegacy customer;

    public CustomerAdapter(ICustomerLegacy customer)
    {
        this.customer = customer;
    }

    public string FullName
    {
        get
        {
            return this.customer.FirstName + " " + this.customer.LastName;
        }
    }

    public int Age
    {
        get
        {
            return DateTime.UtcNow.Year - this.customer.Birthday.Year;
        }
        set
        {
            // your logic here.
        }
    }

    public void Save()
    {
        //this.customer.DoSomething();
    }
}

As you can see, Adapter Pattern can help you refactor existing product, make it testable, and tidy up the legacy code in one place.

Ekk
  • 5,627
  • 19
  • 27
0

As far as I can see you should be able to use a simple approach to handle this situation, with a factory and a interface,

class IStore
{
   void Place(Order order);

   ....other methods
}

class AmazonStore : IStore
{
   ...implement methods
}

class EbayStore : IStore
{
    ...implement methods
}

Here the Order object should be translated to the specific API inside the actual implementation and also the other way round for any methods fetching data.

and then a factory can be used to create the relevant store based on either a constant or based on Enum.

Low Flying Pelican
  • 5,974
  • 1
  • 32
  • 43
0

In one of my previous companies, we worked on similar project. We adopt the Pipe and filters architecture.

Use the Pipes and Filters architectural style to divide a larger processing task into a sequence of smaller, independent processing steps (Filters) that are connected by channels (Pipes).

Each filter exposes a very simple interface: it receives messages on the inbound pipe, processes the message, and publishes the results to the outbound pipe. The pipe connects one filter to the next, sending output messages from one filter to the next. Because all component use the same external interface they can be composed into different solutions by connecting the components to different pipes. We can add new filters, omit existing ones or rearrange them into a new sequence -- all without having to change the filters themselves. The connection between filter and pipe is sometimes called port. In the basic form, each filter component has one input port and one output port.

Think of it as your Source is the Pump, your Translator is the Filter. Any functionality put in the filters will be driven from the corresponding business domain. The big picture looks like this:

enter image description here

ekostadinov
  • 6,880
  • 3
  • 29
  • 47