Background
My project performs software application management; it covers in-app purchasing, product provisioning, account management, etc. I've recently been tasked to add a cost reporting component.
Code
On the code level, I've spread out my model into a few classes that represent categories or interest areas, each requiring specific dependencies. Here's a rough outline:
models/base.class.php
namespace Models;
class Base
{
protected $db; // PDO
public function __construct(PDO $db)
{
}
// helper functions such as data mappers for one or multiple rows
}
models/ordermodel.class.php
namespace Models;
class OrderModel extends Base
{
protected $gateway; // payment gateway to handle purchases
public function __construct(PDO $db, Gateway $gateway)
{
parent::__construct($db);
$this->gateway = $gateway;
}
// methods for purchase, refund, subscription cancellation, etc.
public function updateInstallDetails(...);
public function refundPayment(...);
}
models/provisionmodel.class.php
namespace Models;
class ProvisionModel extends Base
{
protected $account; // user account to "anchor" queries against
public function __construct(PDO $db, Entities\Account $account)
{
parent::__construct($db);
$this->account = $account;
}
// this method gets called when a software update took place
// it's called (indirectly) from a controller class
private function updateInstallDetails(...);
}
The individual classes are accessed via a factory:
class Model
{
private static $repository = array();
/**
* Generic instance loader
*
* @param $name string
* @param $fn callback generates the object instance
*/
private static function getModelInstance($name, $fn)
{
if (!isset(self::$repository[$name])) {
self::$repository[$name] = $fn();
}
return self::$repository[$name];
}
public static function getOrderModel($gateway = null)
{
// TODO pass payment gateway object
return self::getModelInstance(__FUNCTION__, function() use ($gateway) {
if ($gateway) {
$gateway = GatewayFactory::load($gateway);
}
return new Models\OrderModel(SiteConfig::getDefaultDatabase(), $gateway);
});
}
}
Problem
The cost reporting component cross-cuts two main areas of the model, i.e. purchases, refunds and provisioning (shown above). Purchases and refunds of any product are recorded for royalty payments and provisioning keeps track of software updates (between versions there may be added or removed royalties).
My possible solutions
So I have a few options:
Make both my classes aware of the reporting unit; this means extending the constructor and what not.
Apply Decorator pattern to "intercept" method calls.
Apply
ObserverMediator pattern; each class will emit "interesting" events and the reporting unit will listen for them and make the appropriate changes to the database tables.
Questions
Does it make sense to spread out my model into separate classes in the first place? Would it make more sense to make one big class that encompasses all my application logic?
ObserverMediator pattern has its pros and cons; the advantage is that you can easily decouple units from each other, but the downside is that it's indeed decoupled. Are there other cons?
Is there another pattern I could apply to crack this? Is there some other major issue with my code?