Clean Architecture (Entities, Boundaries, Interactors) in PHP ?

1,904 views
Skip to first unread message

George Fekete

unread,
Sep 9, 2013, 6:49:49 AM9/9/13
to clean-code...@googlegroups.com
Hello,

Could somebody point me to a concrete example on how entities, boundaries and interactors are implemented in PHP?

I know the theory (80%), but having a hard time implementing it in PHP, especially if I want to throw an MVC framework (Kohana) into the mix where there are validation, database, caching, etc. modules.
How could I use these modules and still keep a strong separation in my application?

Thanks!

Jop van Raaij

unread,
Sep 9, 2013, 3:04:47 PM9/9/13
to clean-code...@googlegroups.com
I've also looked, but did not find.

If you create an open source example application, I'd be interesting to look and maybe contribute. A simple, useful application with a few re-usable modules would be nice. Using the concept of boundary, interactors and entities should provide modules which can be used in other applications. I would be interested in an authentication or authorization module.

I think it is key you don't use any framework at first. The framework should be a detail: it should be possible to switch from Kohana to Yii, symfony or Zend without changing your application code. Of course you need to change a bunch of boundary code when switching.

George Fekete

unread,
Sep 9, 2013, 4:20:49 PM9/9/13
to clean-code...@googlegroups.com
I would like to see real world examples, like what happens when you fill out a form with validation errors and after correcting the form that data is saved in a database somewhere...

Or some examples along those lines...

Sebastian Gozin

unread,
Sep 9, 2013, 6:15:05 PM9/9/13
to clean-code...@googlegroups.com
I'll try to give an example. Bear in mind though that I haven't written any PHP in 10 years, it's semi pseudo code and I hadn't heard ot Kohana before I read this thread. Also there are many things I did not touch, some rules I broke and improvements which can be made. I'll leave those for further discussion.

So here goes:

A simple controller which declares 1 action to request for the timeline of a specific user.
The input is a username query parameter which is passed to a LoadTimeline usecase upon execution. This is the request model boundary.
You'll also find the controller implements some callback functions as part of the response model boundary. They are unauthenticated, rejected and render_timeline.

class TimelineController extends Controller {
 
public function action_timeline() {
   
LoadTimelineFactory::usecase->execute($this->request->get('username'), $this);
 
}

 
public function unauthenticated() {
   
// ask the user to authenticate? e.g: HTTP 401
 
}

 
public function reject($violations) {
   
// render violations e.g: HTTP 412
 
}

 
public function render_timeline($tweets) {
    $this
->response->body(View::factory('twitter/timeline')->set('tweets', $tweets));
 
}
}

The controller above uses a factory to create an instance of the LoadTimelineUsecase. You could technically deploy a different implementation of this factory to create a different implementation of the usecase.

class LoadTimelineFactory {
 
public function usecase() {
   
return new LoadTimelineUsecase();
 
}
}


So for the usecase we do the following:
  • check if the user is authenticated (on fail respond with unauthenticated)
  • check if the input is valid (on fail respond with rejected and a data structure describing the violations)
  • load the timeline from the tweet gateway and pass it to the render_timeline callback (technically an ISP violation here)

class LoadTimelineUsecase {
  $tweetGateway
= TweetGatewayFactory::gateway();
  $securityPass
= SecurityPassFactory::pass();

 
public function execute($username, $response) {
   
if($securityPass->isAuthenticated()) {
     
if(is_empty($username))
        $response
->reject_input(array('username' => array('required')));
     
else
        $response
->render_timeline($tweetGateway->timelineFor($username))
   
} else
      $response
->unauthenticated();
 
}
}

A factory for a Kohana based security pass collaborator. In a different framework you would make a different implementation naturally.

class SecurityPassFactory {
 
public function pass() {
   
return new KohanaSecurityPass();
 
}
}


The kohana security pass collaborator implementation simply wraps the existing kohana auth API. None of the core business logic directly depends on this. They instead depend on an implicit interface which exposes an isAuthenticated function. Do note that I did not check what the kohana security API actually looks like. Still it is likely it uses different names for its methods so you want to translate those to a common vocabulary the rest of your system can depend on.

class KohanaSecurityPass {
 
public function isAuthenticated() {
   
return Auth::instance()->is_logged_in()
 
}
}

Once again a factory but this time for the tweet gateway.

class TweetGatewayFactory {
 
public function gateway() {
   
return new KohanaTweetGateway();
 
}
}

A tweet gateway implementation which uses the kohana ORM layer to load the timeline useing a named query called timelineFor which takes a username as argument.
Before returning the query results I'm mapping the array to instances of KohanaTweet which extends the Tweet business entity and brokers between the kohana ORM API and the actual entity format you decided upon.

class KohanaTweetGateway {
 
public function timelineFor($username) {
   
return array_map(
     
function($it) {
        $tweet
= new KohanaTweet();
        $tweet
->delegate = $it;
       
return $tweet;
     
},
      ORM
::factory('Tweet')->where('username', $username)->find_all()
   
)
 
}
}


class KohanaTweet extends Tweet {
  $delegate
}

class Tweet {
 
// business rules
}

I hope this helps.
For the most part it's the same s in other languages I'd think.

Cheers,
- Sebastian

Jop van Raaij

unread,
Sep 10, 2013, 2:07:18 AM9/10/13
to clean-code...@googlegroups.com
Thank you for the effort of creating an example, Sebastian! The code looks good :).

It would be nice to see something like this 'grow' using tdd in a open repository (like github). To see the implementation of a concept is one thing, but seeing the code get there is even a lot more interesting. When committing every test with the code-to-pass-the-test and every refactor step afterwards in a seperate commit, it is possible to understand and follow the process of software development. I'd recommend combine the test and code-to-pass-the-test in one commit to make the intent of the code clear.

Sebastian Gozin

unread,
Sep 10, 2013, 4:25:39 AM9/10/13
to clean-code...@googlegroups.com
Perhaps this is something you could try now that you have an idea of the general direction and see where that leads?

FWIW, I just penned down the general structure of how I structured my code in my own apps when I first started to try this. As I kept working on it some things have moved a bit such as I'm using a command chain to deal with preconditions such as auth and permission required, input validation and transformation as well as post conditions such as event handling. So most of the time even those are not part of the general usecase script I actually have to implement.

George Fekete

unread,
Sep 10, 2013, 4:53:16 AM9/10/13
to clean-code...@googlegroups.com
I will create a public repo this week and paste it here, maybe we could collaborate on it so we would have a working example, because lot's of other people, especially in the Kohana framework community are interested.

witali mik

unread,
Sep 10, 2013, 6:30:13 AM9/10/13
to clean-code...@googlegroups.com
Well the developer of Kohana, created a blog post , maybe it worth to look at it?

http://zombor.github.io/blog/2012/09/25/well-designed-application-architectures-part-1/

Dave Marshall

unread,
Sep 11, 2013, 3:15:06 AM9/11/13
to clean-code...@googlegroups.com
Hey

I put together something with a friend for a conference talk we did, it's far from complete, but you should be able to get some ideas. 


That effort focused on a specific request -> response DTO from the interactors, I'm now putting together something that does the callback style that people seem to favour:

github,com/davedevelopment/clean-oo-php <- no TDD in here, it's one big spike so far

Disclaimer: I'm still learning, but I'm sure there could be something in there to help others.

Regards

Dave M

Georgef

unread,
Sep 11, 2013, 3:25:31 AM9/11/13
to clean-code...@googlegroups.com
Thanks for this, I will look over them later on.

Sebastian Gozin

unread,
Sep 11, 2013, 5:51:20 AM9/11/13
to clean-code...@googlegroups.com
Do people favor the callback style? I know I used it in my example earlier but my colleagues at work are usually incredibly resistant about it saying the indirection is too confusing to the point that they appear to prefer SOLID violations. This is a Java environment though and I notice a younger audience seems to adapt to different styles better.

Roberto Guerra

unread,
Sep 12, 2013, 10:11:40 PM9/12/13
to clean-code...@googlegroups.com
I personally prefer the callback style better. But then again, I am doing mostly Javascriipt/Coffeescript these days. There are languages where it is easier to use callbacks, and others where it obviously hurt readability (like Java). It depends on the context. 
Reply all
Reply to author
Forward
0 new messages