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. While designing a quite complicated system with many different functions – I tried to come up with an elegant solution. The main goal was to encapsulate different logic and avoid expensive spaghetti code, which is difficult to maintain. The following solution is worked very well on my last project, so I decided to write this post. The below example is simplified – I am focusing on the idea only. Maybe in future posts, I will extend this simple scenario.

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
    {
        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 the 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 program works well, feel free to extend and customize it to fit your needs. Source code is avaliable here

You may also like...