PHP: Design pattern for managing types of entity -


i have user entity. have multiple "types" of entity, different managers , repositories. user entities of types share userinterface. now, i'm looking method of organizing everything. first thing came mind create this:

interface usertypemanagerinterface {   public function addusertype($name, repositoryinterface $repository, managerinterface $manager);   public function hastype($name);   public function getrepository($type);   public function getmanager($type); } 

then in places manage multiple types of user @ once, inject this, , in places manage specific type of user, inject specific repository , manager objects, type.

seems pretty clean approach, @ same time, when create test class using usertypemanager need mock usertypemanager, mock need return other mocks (repository , manager).

this doable of course, made me thinking if can avoided. other thing can think of, allow avoidance of above complexity during testing, this:

interface usertypemanagerinterface {     public function addusertype($name, repositoryinterface $repository, managerinterface $manager); }  /**  * class managing multiple types of user.  */ class managemultipletypesofusers implements usertypemanagerinterface {     // ... } 

so add repositories , managers classes implementing usertypemanagerinterface interface. objects use directly given them.

this way testing cleaner, because need mock 1 manager , 1 repository test class managemultipletypesofusers, feels sooo over-engineering. ;)

is middle-ground here possible?

i can not give definitive answer on trade-off.

as far see user pure value object? that's start. means it's trivial manipulate without side-effects.

now, consider these variables shall influence design:

  • is interface 1 method? [or methods trivial wrappers 1 method?]
  • is interface meant have users of library define custom implementations?
  • do implementations add assertions (e.g. interface requires method delete(user $user) - admins allowed, other implementations throw, basic permission checks) or use vastly different code?
  • how over-engineered vs untestable mess have?

so, how these variable influence our decision?

  • if ever have 1 [smaller] central method, interfaces plus classes overkill , tend litter codebase many small files. passing closure around achieves same effect.
  • apart when simple closures/callables enough, public api shall interfaces.
  • when implementations add assertions, typically better off 1 single class dispatching request access control manager [which reads/caches permissions database].
  • over-engineered vs untestable: 50 files each 3 lines of different code better merged single file 300 lines.

as not know requirements be, cannot give ideal solution. (second) solution proposed fine "over-engineered" case.

a more moderate solution functional approach:

function addadminuser($name, repositoryinterface $repository, managerinterface $manager) { /* ... */ } function addnormaluser($name, repositoryinterface $repository, managerinterface $manager) { /* ... */ } // can pass around callable ($cb = "addadminuser"), or (pre-binding): $cb = function($name) use ($repo, $mgr) { addadminuser($name, $repo, $mgr); }; 

or radically (if normal user subset of admin user addition) [as long function side-effect free , callers not going hard test]:

function adduser($name, $type, repositoryinterface $repository, managerinterface $manager) {     /* ... common logic ... */     if ($type == is_admin_user) { /* ... */ } } 

if that's radical, possible inject callback adduser:

function adduser($name, $cb, repositoryinterface $repository, managerinterface $manager) {     $user = new user;     /* ... common logic ... */     $cb($user, $repository, $manager); } 

yes, functional approach maybe not testable (i.e. won't able mock function if call directly (without passing callable)), may bring huge gain in readability. saving levels of trivial indirection may make code easier analyze. i'm emphasizing may, removing indirection may achieve opposite effect. find middle ground applicable application.

tl;dr: php provides more functional approach, less testable. that's fine trade-off, not. depends on concrete code middle ground lies.

p.s.: thing really smells bit me in second proposal, having repositoryinterface $repository, managerinterface $manager in addusertype method signature. sure it's not part of constructor?


Comments

Popular posts from this blog

Spring Boot + JPA + Hibernate: Unable to locate persister -

go - Golang: panic: runtime error: invalid memory address or nil pointer dereference using bufio.Scanner -

c - double free or corruption (fasttop) -