chippyash/SDO-Pattern
Quality Assurance
, (*1)
The above badges represent the current development branch. As a rule, I don't push
to GitHub unless tests, coverage and usability are acceptable. This may not be
true for short periods of time; on holiday, need code for some other downstream
project etc. If you need stable code, use a tagged version. Read 'Further Documentation'
and 'Installation'., (*2)
Please note that the Travis build servers sometimes have a wobbly and thus the build
status may be incorrect. If you need to be certain, click on the build status badge
and checkout out the build for yourself., (*3)
See the Test Contract, (*4)
What?
This library supplies the Service Data Object (SDO) pattern. SDOs have a long history and provide a way to abstract
out the process and operation on retrieving and sending data to some remote service endpoint. That endpoint can
be a file, a database, a REST service etc., (*5)
Essentially, you are not interested in where the data resides, only in using it. The SDO pattern abstracts out the
fetching and sending process to allow you to concentrate on using the data in some internalised format., (*6)
There is a long discourse on implementing SDOs
in PHP written by Zend that describes a much more complex SDO infrastructure than is provided in this library. Personally,
I've never had the need to get complicated with SDOs, but it's there if you need it., (*7)
The library is released under the GNU GPL V3 or later license, (*8)
Why?
This pattern has emerged through many years of repeating the same thing:, (*9)
- I need data in a format that I can use it
- The data is located at some endpoint over which I have no control
There are some key elements to a SDO if you ignore the complexity of session storage and potential caching of SDOs
in flight:, (*10)
- You need to have a consistent internal representation of the remote data
- mappers provide this. The mapper turns external format data (xml, json etc) into an internal format (a model
class for instance) that can be referenced by the application. The mapper also maps that internal format back out
to the external format for writing.
- You need to validate that the incoming remote data is conformant
- The remote endpoint may change over time
- transports facilitate this. Transports can connect to databases, file stores, http endpoints etc.
When
The current library provides the basic tools for creating SDOs, primarily the required interfaces. Also included
is an abstract SDO which you can extend for your concrete implementation. Simple mappers, validators and transports are
provided. An example script is also provided to give you a flavour of how to implement them., (*11)
If you want more, either suggest it, or better still, fork it and provide a pull request., (*12)
Check out ZF4 Packages for more packages, (*13)
How
Coding Basics
An SDO requires three things in order to operate effectively:, (*14)
- a TransportInterface (Transport) to do the actual fetching and sending of data from and to the service endpoint
- a MapperInterface (Mapper) to map the external data into something your application can use, and to map that internal
representation back out as something the service endpoint understands
- a ValidatorInterface (Validator) so that you can be assured that incoming data meets your application's requirements
The following is based on a simple scenario:, (*15)
- data is contained in a file that is provided by some other system over which we have no control
- external data format is json, the internal format is a StdClass
- we need a mapper that maps incoming json to StdClass and back out as Json
- external data needs to match a specific minimal pattern to be valid
- we need a validator that checks minimum requirements
Transport
The TransportInterface dictates two methods:, (*16)
-
public function read();, (*17)
- read data from a remote endpoint
-
public function write($data);, (*18)
- write data to a remote endpoint
The supplied chippyash\SDO\Transport\File gives us what we need, and takes a single construction parameter, the path
to the file., (*19)
Mapper
The MapperInterface dictates two methods:, (*20)
-
public function mapIn($data);, (*21)
- map some data fetched by TransportInterface::read() into some internalised format
-
public function mapOut($internal);, (*22)
- map internal formatted data to some external format to be used by TransportInterface::write()
The supplied chippyash\SDO\Mapper\Json gives what we need., (*23)
Validator
The ValidatorInterface dictates one method:, (*24)
- public function isValid($external);
- validate the external format data read by TransportInterface::read() and return true if valid else false.
The chippyash\SDO\Validator\Passthru validator simply returns true for any data it is asked to validate, and is used
for our example., (*25)
SDO
The SDOInterface dictates, (*26)
- public function fetch();
- fetch SDO data from remote source, validate it and map it into internalised format
- public function send();
- convert internal format data to external format and send it to remote target
- public function getData();
- public function setData($incomingData);
- set the SDOs internal data structure directly
- public function setMapper(MapperInterface $mapper);
- set the mapper for the SDO
- public function setTransport(TransportInterface $transport);
- set the transport for the SDO
- public function setValidator(ValidatorInterface $validator);
- set the validator for the SDO
For our example we can create a simple SDO by extending the AbstractSDO and constructing it with, (*27)
class FooSDO extends AbstractSDO {}
$sdo = new FooSDO(
new FileTransport(__DIR__ . '/test.json'),
new JsonMapper(),
new PassthruValidator()
);
To read and write data we can use:, (*28)
$obj = $sdo->fetch()->getData();
$obj->bar += 1;
$sdo->send();
The AbstractSDO also supports proxying the getData() method via the magic __invoke method, so we can also read and write
thus:, (*29)
$sdo->fetch();
$sdo()->bar += 1;
$sdo->send();
This clearly is closer to true SDO usage., (*30)
It is not beyond the wit of a PHP dev to create a descendent SDO that is
totally self managing, caching locally when required, fetching and sending only when required. In my day job, we have
all of this and service classes that manage whole collections of SDOs. Numb nuts upstream change the incoming payload;
we just change the validator (if necessary, see below). Change the endpoint; we change the transport. Want to change the way you
represent the data internally; change the mapper., (*31)
On Mappers, consider using the Assembly Builder or Builder Pattern if you need to create
complex data structures., (*32)
On Validators, code defensively. Validate only what you expect to use and ignore the rest. That way, when they change the
payload without telling you, you don't care (assuming they leave what you want in it!).
Consider using Functional Validation, (*33)
You can find the source in example/example.php., (*34)
Changing the library
- fork it
- write the test
- amend it
- do a pull request
Found a bug you can't figure out?, (*35)
- fork it
- write the test
- do a pull request
NB. Make sure you rebase to HEAD before your pull request, (*36)
Alternatively, raise a ticket in the issue tracker, (*37)
Where?
The library is hosted at Github. It is
available at Packagist.org, (*38)
Installation
Install Composer, (*39)
For production
add, (*40)
"chippyash/sdo-pattern": ">=3,
to your composer.json "requires" section
#### For development
Clone this repo, and then run Composer in local repo root to pull in dependencies
git clone git@github.com:chippyash/SDO-Pattern.git chippyash-sdo
cd chippyash-sdo
composer install
To run the tests:, (*41)
cd chippyash-sdo
vendor/bin/phpunit -c test/phpunit.xml test/
License
This software library is released under the BSD 3 Clause license, (*42)
This software library is Copyright (c) 2015-2018, Ashley Kitson, UK, (*43)
History
V1.0.0 Original release, (*44)
V2.0.0 BC Break: namespace change from chippyash\SDO to Chippyash\SDO, (*45)
V2.0.1 update examples, (*46)
V2.0.2 Add link to packages, (*47)
V2.0.3 verify PHP7 compatibility, (*48)
V2.0.4 update build scripting, (*49)
V3.0.0 BC Break. Withdraw support for PHP <5.6, (*50)
V3.1.0 Change of license from GPL V3 to BSD 3 Clause, (*51)