Updating an Entity through a Service
- by GeorgeK
I'm separating my software into three main layers (maybe tiers would be a better term):
Presentation ('Views')
Business logic ('Services' and 'Repositories')
Data access ('Entities' (e.g. ActiveRecords))
What do I have now?
In Presentation, I use read-only access to Entities, returned from Repositories or Services, to display data.
$banks = $banksRegistryService->getBanksRepository()->getBanksByCity( $city );
$banksViewModel = new PaginatedList( $banks ); // some way to display banks;
// example, not real code
I find this approach quite efficient in terms of performance and code maintanability and still safe as long as all write operations (create, update, delete) are preformed through a Service:
namespace Service\BankRegistry;
use Service\AbstractDatabaseService;
use Service\IBankRegistryService;
use Model\BankRegistry\Bank;
class Service
extends AbstractDatabaseService
implements IBankRegistryService
{
/**
* Registers a new Bank
*
* @param string $name Bank's name
* @param string $bik Bank's Identification Code
* @param string $correspondent_account Bank's correspondent account
*
* @return Bank
*/
public function registerBank( $name, $bik, $correspondent_account )
{
$bank = new Bank();
$bank
-> setName( $name )
-> setBik( $bik )
-> setCorrespondentAccount( $correspondent_account );
if( null === $this->getBanksRepository()->getDefaultBank() )
$this->setDefaultBank( $bank );
$this->getEntityManager()->persist( $bank );
return $bank;
}
/**
* Makes the $bank system's default bank
*
* @param Bank $bank
* @return IBankRegistryService
*/
public function setDefaultBank( Bank $bank )
{
$default_bank = $this->getBanksRepository()->getDefaultBank();
if( null !== $default_bank )
$default_bank->setDefault( false );
$bank->setDefault( true );
return $this;
}
}
Where am I stuck?
I'm struggling about how to update certain fields in Bank Entity.
Bad solution #1: Making a series of setters in Service for each setter in Bank; - seems to be quite reduntant, increases Service interface complexity and proportionally decreases it's simplicity - something to avoid if you care about code maitainability. I try to follow KISS and DRY principles.
Bad solution #2: Modifying Bank directly through it's native setters; - really bad. If you'll ever need to move modification into the Service, it will be pain. Business logic should remain in Business logic layer. Plus, there are plans on logging all of the actions and maybe even involve user permissions (perhaps, through decorators) in future, so all modifications should be made only through the Service.
Possible good solution: Creating an updateBank( Bank $bank, $array_of_fields_to_update) method; - makes the interface as simple as possible, but there is a problem: one should not try to manually set isDefault flag on a Bank, this operation should be performed through setDefaultBank method. It gets even worse when you have relations that you don't want to be directly modified.
Of course, you can just limit the fields that can be modified by this method, but how do you tell method's user what they can and cannot modify? Exceptions?