Simple Backend solution based on Command Pattern

In this post I would like to present a simple backend system based on the Command Design Pattern where each command has own independent scope.
Using the encapsulated logic we avoid extensive spaghetti code. The following solution is quite elegant and worked very well in my last project and it was more advanced. The below example is poor and I am focused on idea only, but in future posts I will try extend this simple scenario. Ok, let’s get to work.

Our heroid cast consists of:

  • CommandBase (Command) – declares an interface for executing an operation
  • ConcreteCommand (ImportCommand, NotificationCommand) – defines a binding between a Receiver object and an action. Implements Execute by invoking the corresponding operation(s) on Receiver
  • Client (MainApp) – creates a ConcreteCommand object and sets its receiver
  • CommandInvoker – execute the commands (Processor)
  • ReceiverBase – base code for concrete receivers, setting some parameters
  • ConcreteReceiver – implements custom logic (ImportReceiver, CommandReceiver)

CommandBase

 
public abstract class CommandBase
    {
        protected readonly ReceiverBase Receiver;
        public CommandBase(ReceiverBase receiver)
        {
            Receiver = receiver;
        }
        public abstract void Execute();
    }

ImportCommand

 
    public class ImportCommand : CommandBase
    {
        public ImportCommand(ReceiverBase receiver)
            : base(receiver)
        {
        }

        public override void Execute()
        {
            Console.ForegroundColor = ConsoleColor.Green;

            var importReceiver = Receiver as ImportReceiver;
            if (importReceiver == null) throw new ArgumentNullException("Import Receiver is null !");

            importReceiver.ProceesImport();
        }
    }

NotificationCommand

 
  public class NotificationCommand : CommandBase
    {
        public NotificationCommand(ReceiverBase receiver)
            : base(receiver)
        {
        }

        public override void Execute()
        {
            Console.ForegroundColor = ConsoleColor.Cyan;

            var notificationReceiver = Receiver as NotificationReceiver;
            if (notificationReceiver == null) throw new ArgumentNullException("Notification Receiver is null !");

            notificationReceiver.SendNotifications();
        }
    }

ReceiverBase

 
 public abstract class ReceiverBase
    {
        protected IDataContextFactory DataContextFactory;
        protected object[] Parameters;

        public ReceiverBase(IDataContextFactory dataContextFactory, params object[] parameters)
        {
            DataContextFactory = dataContextFactory;
            Parameters = parameters;
        }
    }

ImportReceiver

 
    public class ImportReceiver : ReceiverBase
    {
        public ImportReceiver(IDataContextFactory dataContextFactory, params object[] parameters)
            : base(dataContextFactory, parameters)
        {
        }

        public void ProceesImport()
        {
            using (IUnitOfWork unitOfWorkDataContext = DataContextFactory.GetNewDataContext())
            {
                StartImport();
                FinishImport();

                unitOfWorkDataContext.Commit();
            }
        }

        private void StartImport()
        {
            Console.WriteLine(" ImportReceiver: StartImport() ");
        }

        private void FinishImport()
        {
            Console.WriteLine(" ImportReceiver: EndImport()");
        }
    }

NotificationReceiver

 
  public class NotificationReceiver : ReceiverBase
    {
        public NotificationReceiver(IDataContextFactory dataContextFactory, params object[] parameters)
            : base(dataContextFactory, parameters)
        {
        }

        public void SendNotifications()
        {
            using (IUnitOfWork unitOfWorkDataContext = DataContextFactory.GetNewDataContext())
            {
                SendSMS();
                SendEmails();

                unitOfWorkDataContext.Commit();
            }
        }

        private void SendEmails()
        {
            Console.WriteLine(" NotificationReceiver: SendSMS()");
        }

        private void SendSMS()
        {
            Console.WriteLine(" NotificationReceiver: SendEmails()");
        }
    }

CommandInvoker

 
    public class CommandInvoker
    {
        //* Some declaration... I just found a bug in sh plugin for highlighting code ;] *//

        public void SetCommand(CommandBase command)
        {
            _commands.Add(command);
        }

        public void ExecuteCommands()
        {
            foreach (var command in _commands)
            {
                command.Execute();
            }
        }
    }

Now we are ready to go, so let’s put code together and do some serious damage!

 
static void Main(string[] args)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("\n Command service Start");

            IDataContextFactory dataContextFactory = new DataContextFactory();
            var importCommand = new ImportCommand(new ImportReceiver(dataContextFactory));
            var notificationCommand = new NotificationCommand(new NotificationReceiver(dataContextFactory));

            var commandInvoker = new CommandInvoker();

            //Hook up with commands
            commandInvoker.SetCommand(importCommand);
            commandInvoker.SetCommand(notificationCommand);

            commandInvoker.ExecuteCommands();

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(" Job done");
            Console.WriteLine(" Command service Stop");
            Console.ReadKey();
        }

Execute result
Command pattern backend solution Piotr Łuksza

As we see programn works well, we can extend and customize it to suit our needs, anyway in the future I will try to show you some new cool possibilities.

Source code is avaliable here

You may also like...