timehunter / delivery-order-test
:description
Requires
Requires (Dev)
- mockery/mockery: ^1.1
- orchestra/testbench: ~3.0
- php-coveralls/php-coveralls: ^2.1
- phpunit/phpunit: ~7.0
- sempro/phpunit-pretty-print: ^1.0
This package is auto-updated.
Last update: 2024-12-15 13:15:12 UTC
README
15th March 2019 Update
I just quickly added a new alternative approach by using Strategy pattern under DeliveryOrderModule/Strategy folder. Strategy pattern is good to be used when the class needs to be fully decoupled and distributed to anywhere.
Note: I haven't added test for Strategy pattern yet. Honestly, I still prefer Template method, all the duplicate code put into super class so that all subclass share those code. And the requirement clearly states each of delivery order has its own workflow which is a perfect usage to define steps in super class (AbstractDeliveryOrderProcessor)
(Strategy pattern is just a bonus showcase and it doesn't have test etc....)
Description
The solution is written on Laravel framework.
Installation
- This is a Laravel composer package, you need to have a Laravel installed first.
- Via Composer
$ composer require timehunter/delivery-order-test
Question Analysis
Potential design patterns can be used for this problem: factory, template method and strategy
The problem can be categorised as the following points:
- How to create different objects based on dynamic input value? e.g. “enterpriseDelivery” return EnterpriseDelivery object
- use Factory pattern which encapsulates the logic of creating objects.
- Different object should contain its own workflow and dependent services.
- use Strategy pattern(interface) or template method(inheritance)
- Domain driven design
- Basically make the system as module based and each module has its own services. If there are any common interfaces, they can be shared across different module.
- From the question mentioned, I can see there are three modules can be found: Delivery Order module, Third party module and Marketing module
My solution
Assumptions
- Marketing service and Third Party api service have been fully tested.
Designs
- I use Template Method design pattern which is based on inheritance. It allows me modify parts of an algorithm by extending those parts in sub-classes.
- I also use factory pattern to determine which type of delivery order object I need to return.
Note:
- As mentioned in the question, the EnterpriseDelivery Order needs to be validated with third party api first, if its failed, the logic should no longer continue. In this case, I have a defined steps in the abstract class, e.g. check validation first, then do the logic.
.... public function process() { if ($this->validate()) { $this->before(); $this->handle(); $this->after(); return true; } else { return false; } } ....
The handle function is an abstract function which will be implemented for all sub-classes. Use this function to define its own workflow.
Usage
$json = "[{},{},{}...{}]"; // the given sample json data $service = new DeliveryOrderService($json); $objects = $service->processJson() // this return a list of different delivery order objects
Testing
- use PHPUnit
- Mocking Interfaces for testing
- Pass different json to test if the service returns the correct delivery type
- use travis-ci for CI/CD: https://coveralls.io/github/RyanDaDeng/delivery-order-test?branch=master
Improvements & Relecftions
- The package does not use Laravel feature e.g. service containers, facades. They can be applied by creating service provider to register their dependencies.
- API service and marketing service can be singleton.
- If any new delivery order type comes in, we just need to create a new sub-class which extends Abstract base class without touching source code (open for extension).
- Strategy pattern can be used instead of inheritance if the interface and design are finalised.
- Exception handler can be implemented on the top of abstract class.