Developer & Human. Finding my voice.

Category: programming

An Example of Event Sourcing

I’ve been working on a new talk to describe a project where we are replacing an application that used a complex list of statuses to describe where a student’s enrollment request is in the process.  Examining older applications like this one, you easily see how they closely mimicked the old paper form processes.  The status of the paper form was directly tied to its being added to a stack of papers.  Over time, the approval process became more complex, and the number of piles or statuses increased.  The status drop-down in this application became REALLY long!  Before we begin examining a lot of statuses, I’d like to discuss the simple example.  After a Student submits their Enrollment Request, an Admin either Approves or Rejects their Request.

Bounded Contexts

Our team has built applications using DDD.  We recognize the bounded contexts of Student and Admin spaces in the code as well as in the user interfaces.  Without making this overly complicated, we know that there are some actions a Student makes in this system and at a point, they are just waiting to hear back from the Admin.  There are a completely different set of things the Admin does in this space.  When we create a bounded context, we let the code mirror their real-life processes, without muddying the concerns of the other bounded context.  There is no scenario where a Student can Approve or Reject an Enrollment Request, so we will begin there.

Events to Approve and Reject an Enrollment

A diagram showing a form arrives and is either approved or rejected

A student Submits an Enrollment Request and then waits to hear what has happened to it.  The Admin may request more information from the Student before they can approve or reject.  But on a very basic example, the student submits an Enrollment Request and waits for a notice of Approve or Reject.  The Approve or Reject Events will trigger a message to the student and probably some other things.  Our Admin receives the request and clicks a button, either Approve or Reject.  When they click, a corresponding event is created.  The event is a class like this:

public class EnrollmentRequestApproved implements Event
    // @var UUid
    public $enrollmentRequestId;

    // @var StudentId
    public $studentId;

    // @var CourseOfferingId
    public $courseOfferingId;

    public function __construct(Uuid $enrollmentRequestId, StudentId $studentId, CourseOfferingId $courseOfferingId)

        $this->enrollmentRequestId = $enrollmentRequestId;
        $this->studentId = $studentId;
        $this->courseOfferingId = $courseOfferingId;

All Events are stored in an Event Store, and they are stored in the order they are recorded.  We can listen for Events arriving in the Event Store, and trigger certain things to happen based on those Events.  Let’s say the Admin wants a list of all Enrollment Requests that haven’t been Approved or Rejected, so they can enroll the students in classes they need.  We’ll call this Enrollment Request Projector.  It will maintain a list of all Enrollment Requests.  When a student submits their request for enrollment, our Projector will add it to the list of Enrollment Requests.

The Admin Dashboard will display all requests in this table. The beforeReplay() and afterReplay() methods allow us to easily rebuild the Projection based on new information.  The apply… methods contain the changes that are made to the table based on the Event.A new StudentRequestedEnrollment event will add a Enrollment Request with a status of “requested”.  But when the admin approves or rejects the request, that corresponding Event will be created.  When our Projector detects the Approve or Reject event, the corresponding Enrollment Request will be removed from the table.

class EnrollmentRequestProjector extends SimpleProjector implements Replayable

     * @var Connection
    private $connection;

     * @var string table we're playing events into
    private $table = 'proj_enrollment_requests';

    public function __construct(Connection $connection)
        $this->connection = $connection;

    public function beforeReplay()
        $builder = $this->connection->getSchemaBuilder();

        $builder->create('proj_enrollment_requests_tmp', function (Blueprint $schema) {


        $this->table = 'proj_enrollment_requests_tmp';


    public function afterReplay()
        $builder = $this->connection->getSchemaBuilder();

        $builder->rename('proj_enrollment_requests_tmp', 'proj_enrollment_requests');

        $this->table = 'proj_enrollment_requests';

     * @param StudentRequestedEnrollment $event
    public function applyStudentRequestedEnrollment(StudentRequestedEnrollment $event)
        $enrollmentRequest = new EnrollmentRequest;

        $enrollmentRequest->id = $event->enrollmentRequestId->toString();
        $enrollmentRequest->student_id = $event->studentId->toNative();
        $enrollmentRequest->course_offering_id = $event->courseOfferingId->toNative();
        $enrollmentRequest->status = 'requested';


     * @param EnrollmentRequestApproved $event
    public function applyEnrollmentRequestApproved(EnrollmentRequestApproved $event)
        $enrollmentRequest = EnrollmentRequest::where('id',  $event->enrollmentRequestId->toString())

     * @param EnrollmentRequestRejected $event
    public function applyEnrollmentRequestRejected(EnrollmentRequestRejected $event)
        $enrollmentRequest = EnrollmentRequest::where('id',  $event->enrollmentRequestId->toString())

This is a long piece of code to review.  But I hope that it will demonstrate how the events work.  I am still doing quite a bit of hand-waving around the event store and some of our classes that we are using in this project.  I apologize for that.  The part I want you to understand, the useful take-away is that these events can be applied in different contexts of the application.  For the Admin Dashboard, the EnrollmentRequestRejected and EnrollmentRequestApproved has one effect on the Enrollment Request projection.  But if we wanted a view of the Closed Enrollment Requests, we could apply those same events in an opposite way.  Likewise, the Student Dashboard has its own sets of methods that apply the events that are relevant.  From this context, the events are applied differently.  A Student wants to see the status of their requests, and their aim is to be Enrolled, which is actually a step beyond Approved.  An Event tied to the Registration System will let us know that they are enrolled in the course, and completes this part of the example.






What Community Means to Me

Ever since I began attending conferences again, there have been several things bugging me.  Primarily it’s that so little has changed since I started college and later attended my first conference in 2001.  I ALWAYS bristled when a speaker would give a long list of their fellow speaker friends held up as community leaders.  There is no way to list every speaker in that list, so it seemed to focus on that person’s friends.  I knew the omitted speakers would feel left out, but I also thought about everyone else in the room who was definitely a member of the community who could feel left out in this type of talk.  It’s taken me a while to put my finger on this, so please bear with me as I tease this idea out.

When did I join the PHP community?

Well, I learned PHP in 1999.  I was a webmaster for a department on campus.  The IT lead, Kevin, said I needed to learn PHP because it was really cool!  It was, and in 2000 I took my first full-time job using PHP to do super cool things on the Internet.  I would say that I became a member of the PHP community when I first took the Webmonkey tutorial and began asking questions and building tools on the Internet.

At conferences, there is this idea that you weren’t a member of the community before you began attending conferences.  This fails to recognize the PHP conference community as a subset of the greater PHP community.  I believe this raises the stage higher than the audience and higher than the developers who use PHP daily.  I believe it puts space between the speaker and the audience.  I don’t like it!

Who is in the PHP community?

The conference community is a much smaller world.  You see a lot of the same people, and friendships form and you can see these people online in between conferences.  It’s fantastic!  And when speakers are traveling from to many conferences, they become even more familiar with each other.  The developer who can only attend a conference each year is less familiar.  This is a tight, supportive community, especially when you are known.  However, I firmly believe:

The PHP community is each one of us who use PHP or supports a PHP tool.  Because when we try to define community as anything less, we’re just forming cliques.

I learned a lot about PHP from 1999 to 2014.  PHP grew and changed A LOT in that time.  When I see the conference community represented as “The PHP Community” it very clearly omits my 15 years of experience.  It leaves out my peers on campus whose bosses don’t value continuing education for their developers.  Being in that space and relying on documentation alone to do your job and bring your skills up is HARD.  People in these legacy spaces are fighting an uphill battle to modernize their applications because the code quality is not valued by the people in charge.

Maybe, after all of this perseverance, they get to attend a conference.  They hear long lists of the new things they should be doing and judgements of how bad it was to do things the old way.  Then a motivating speaker defines community as these other people, and I just think it’s so backwards.

We are Community and So Can You!

I want to hear from the people who inherit these applications.  I want to hear how they’re overcoming challenges!  Because those projects are infinitely more useful to me than a talk about some new tool that isn’t even production stable yet.  I want to see the projects that succeeded and the ones that failed because there is so much learning there.  When we are speaking to people, we should be inspiring them to try something new.  We should be sharing tools they need, empowering them that they are valuable members of our community, and opening the floor to welcome them when they are finally ready to share that big project.

I feel very lucky that there were community members and speakers who encouraged me at conferences.  I know these talks mean to motivate people, and they also include a lot of history and support.  But it always bugs me when anyone labels the PHP Community as anything less than anyone who uses PHP. But I don’t always see that coming from the stage.  And the  stage has a louder voice.  I’d like to see that change a bit!

Bootstrapping the CodeIgniter App, Part Two

Acceptance Testing

At the end of Part One, we were creating a lot of acceptance tests.  We had a pretty antiquated HTML form for the students to fill in a lot of data, some of which we didn’t need to collect from them.  We knew the templates would be changed and had a designer’s help making those look better and sensible.  We knew what data we needed to keep collecting in the application, so our Codeception tests checked that imaginary student’s application had the same information.  We planned knowing that the User Interface was changing and tried to ensure the data integrity.  Codeception tests are pretty brittle when you have to link to a specific DOM element, but we did the best we could to ensure that data wasn’t left off the Student’s Application for Scholarships.


As we began writing the new code, we needed to bind those namespaces to the CodeIgniter application, so the new code could be linked within the application.

We created /app/bindings.php


use ScholarshipsSelectionInfrastructureStorageHybridSourcedScholarshipRepository;

$container['ScholarshipsSelectionDomainScholarshipScholarshipRepository'] = function($c) {
    return new HybridSourcedScholarshipRepository(
); };

The bindings file is used for most of our new functionality in the /src path.  For example, the HybridSourcedScholarshipRepository builds all the current Scholarships with information from our database tables and our Events.  The constructor requires objects of type EventStore, Database, DepartmentsService, ResidenciesService, AcademicPlansService, and CollaborationsService.  We give the paths to these in the bindings for the container ScholarshipRepository.  The container can then be included in the Selection controller.

In /app/services.php, we included the bindings and facades:

 * Facade registration.
class_alias('ScholarshipsSupportFacadesRequest', 'Request');
class_alias('ScholarshipsSupportFacadesDB', 'DB');
class_alias('ScholarshipsSupportFacadesLog', 'Log');

// Bring in application-specific bindings require_once('bindings.php');

Then we created a new file called /src/CowboyBridge.php, which was used to initialize the Pimple Container.


namespace CodeceptionModule;

use CodeceptionModule;

class CowboyBridge extends Module
     * @var PimpleContainer
    private $container;

    public function _initialize()
        require_once(__DIR__ . '/../app/services.php');
        $this->container = $container;
        $this->debug("Loaded container.");

    public function getContainer()
        return $this->container;


Now we needed to be able to see the new code in our controllers.  This means, for example, ,being able to pass our collection of available Scholarships to the Selection controller, and from the controller into the views.  We named our new selection controller Selectionnext in order to leave the old Selection controller intact.

class Selectionnext extends BaseController
    public function Selectionnext()

        $this->scholarshipRepository = $this->container['ScholarshipsSelectionDomainScholarshipScholarshipRepository'];
protected function scholarshipsDashboard($scholarshipId)
    $committeeMember = $this->getCommitteeMember();

    $scholDetails = $this->getScholarshipDashboard($scholarshipId);

    $this->render('pages/scholarship-details.twig', array("scholarship" => $scholDetails)));

private function getScholarshipDashboard($scholarshipId,$keepItLight=false)
 $committeeMember = $this->getCommitteeMember();

 $presenter = new ScholarshipDashboardPresenter(

 $scholDetails = $presenter->asArrayForJson();
 $scholDetails['json_string'] = json_encode($scholDetails);

 return $scholDetails;

I have tried to remove some of the complexity of this part and hopefully not removed anything critical.  We compiled a lot more information into our $scholDetails and sent many more variables to our views.  But this should show a bit of how we mapped the information into the Controllers of CodeIgniter.


Lastly, we changed our views.  Leaving the old views in the /app/views directory as they were in CodeIgniter, we opted to use Twig views for our new interfaces, and we put those files in the /app/templates directory.    We set these paths in our app/config.php

return array( 
 'twig' => array(
   'template_path' => array(
     __DIR__ . '/templates',
     __DIR__ . '/templates/dashboard',


In the file app/services.php we pick up these configured paths and pull them into CodeIgniter.

$container['twig'] = function ($c) {
    $loader = new Twig_Loader_Filesystem($c['config']['twig']['template_path']);

    $inDevelopment = $_SERVER['APP_ENV'] === 'development';

    $twig = new Twig_Environment($loader, array('debug' => $inDevelopment));

    $app = $c['config']['app'];
    if ($inDevelopment) {
        $app['debug'] = TRUE;

    $twig->addGlobal('app', $app);
    $twig->addGlobal('notice', $c['config']['notice']);

    $twig->addFunction(new Twig_SimpleFunction('route', function($path) {
        return site_url($path);

    $twig->addFunction(new Twig_SimpleFunction('asset', function($path) {
        return base_url() . 'public/' . $path;

    $twig->addFunction( new Twig_SimpleFunction('flash_notice', function() {
        if (isset($_SESSION['flash_notice'])) {
            $_SESSION['old_flash_notice'] = $_SESSION['flash_notice'];

        return isset($_SESSION['old_flash_notice']) ? $_SESSION['old_flash_notice'] : array();

    if ($inDevelopment) {
        $twig->addExtension(new Twig_Extension_Debug());

    $twig->addFunction( new Twig_SimpleFunction('flash_exists', function() {
        return !empty($_SESSION['flash_notice']);

    return $twig;

The $_SERVER['APP_ENV'] variable was set in our .htaccess file.  This Twig configuration allows us to get debug information in our development environments.  We added two simple functions to build our asset path and our route path.  This meant we could use route() and asset() in our templates to correct paths across environments.  There are also simple functions to determine whether there are existing flash messages to pass into the template.


This code grew over a long period along with the new functionality of our application.  If you have questions about something that seems missing, please feel free to ask in comments.  I may be able to include a little more code to clarify the details.  Our Scholarships application is pretty complicated, and I’ve tried to skim a lot of that detail out of the code examples in order to simplify and focus on the bootstrapping.  I hope that it has been useful to you because it was really useful for me to re-examine how this all ended up working together.

Bootstrapping the CodeIgniter App, Part One

I have worked on this Scholarships project for about two and a half years.  It’s going to be replaced by another application, so I have reviewed how we did this and what I’ve learned in the process.  Modernizing a CodeIgniter Application – The Problems discusses the problems we were trying to solve (TLDR; we had a legacy CodeIgniter application with a giant App model full of functions making DB calls).  This post shares how we bootstrapped the CodeIgniter application to allow us to bring new code online without breaking the pre-existing application.  The reason we had to do this is also described in the previous post.

It’s important to me that I mention my coworker, Dustin Wheeler, figured out how to do most of this, and I have also learned a lot from Mitch Amiano during this project.  I am trying to document the ways that we modernized this Application to make sure that I am able to do it again.  Most of my development life has involved taking on existing applications, so this seems like a useful bit of information.

Screen Shot of Early Scholarships Working Tree

Screen Shot of Early Scholarships Working Tree

This is what the tree basically looked like at the beginning:

The directories /_data/app, /dinner, and /public already existed.  The bulk of the Application existed in the app directory.  Dinner was another little application on the side.

The first steps included creating a composer.json file that named the application, set the default namespace and mapped that to a new directory called /src.

The initial composer.json configuration file.

Initial composer.json file




Next we installed Phinx for database migrations and PHPUnit and Codeception for tests.  After the bootstrapping process, we used Codeception acceptance tests to verify that the data displayed on the Student Application stayed the same when we built entirely new views and changed the workflow around how the students applied for Scholarships.


In index.php, we turned on error_reporting and required the composer autoloader.

If no file_exists vendor/autoload.php, throw an exception. Otherwise require vendor/autoload.php

Adding composer autoloader to index.php

Next we had a few fixes to CodeIgniter bugs.  We removed a call to set_magic_quotes_runtime() in the file /_data/system/codeigniter/CodeIgniter.php.  In another file /_data/system/codeigniter/Common.php we removed the & from the following statement: $objects[$class] =& new $name();

We built two .sql files for the two databases used by our application.  In these files we saved the create schemas for each of the databases from Production. These files were later populated with anonymized test data, since we couldn’t use anything personally identifiable about our students or faculty.  We kept most of the scholarship data the same, so we could test against real scholarship matching criteria, but we anonymized the contact info in those as well.

We created a debug controller that later went away.  I’ll come back to note its purpose.  I believe it was to test new code from /src being pulled in.

The database configuration had persistent connections turned on, and we set them false:

$db['default']['pconnect'] = false;

$db['default']['db_debug'] = false;

$db['default']['cache_on'] = false;

A base database configuration script became pretty useful for three developers.  It began as a deploy script, but later evolved into a fully incorporated configuration file.  In the same file as above, the /app/config/database.php, we anonymized the hostname, username, password, and database to things like DB_HOST,  DB_USERNAME, DB_PASSWORD,  and DB_NAME.  The deploy script let us set these to something real in our development environment.  I have pasted the bulk of that script, but this script will only configure one database.  It seemed more readable and shorter this way.


# Start Application Configuration
if [[-f app/config/config.php ]]
        echo "Existing Application Config:"
        GREP "APP_" app.config.config.php


echo "Configure Application Config (1/1):"
read -p "Application Base URL ($APP_BASE_URL): " base_url
read -p "Application Index Page ($APP_INDEX_PAGE): " app_index_page


sed "
" app/config/config.default.php > app/config/config.php

echo "Rewrote app/config/config.php"

# Start Database Configuration

if [[ -f app/config/database.php ]]
    echo "Existing Config:"
    grep "$db" app/config/database.php

# Default Configuration Values

echo "Configuring Scholarship Database (1/2):"
read -p "DB Username ($DB_USERNAME):" username
read -p "DB Password ($DB_PASSWORD):" password
read -p "DB HOST ($DB_HOST):" dbhost
read -p "DB Name ($DB_NAME):" dbname


sed "

" app/config/database.default.php > app/config/database.php

echo "Rewrote app/config/database.php"
sed -i "
" app/config/config.php

echo "Updated app/config/config.php because stupid junk."

# Import base schemas for databases.
# Should kick of migrations in future.

# Import schema for default database.
mysql -u ${DB_USERNAME} -p${DB_PASSWORD} -h ${DB_HOST} < database/seeds/default.sql

Modifying CodeIgniter to Allow Testing

In the file /_data/system/libraries/Loader.php, we removed the line from function CI_Loader():

global $OUT;

with the following line:

$OUT =& load_class('Output');

In the file /app/config/config.php we set $config[‘enable_hooks’] to TRUE.

In the file /app/config/hooks.php we added this hook:

$hook['display_override'] = array(
    'class' => 'DisplayHook',
    'function' => 'captureOutput',
    'filename' => 'DisplayHook.php',
    'filepath' => 'hooks'

I’m pretty sure that hook is for Codeception to capture output using PhantomJS.  At this point, we began with a new Codeception test suite for the Student Application.  This part of our Application was mostly changing visually.  When a student completed their application and submitted the information, we created a StudentSubmittedApplication event instead of saving an entire set of data in a database row, as the previous application was working.  But this is getting a little ahead of the game.  We’re bootstrapping here.

In the file /app/hooks/DisplayHook.php, we told it to get the APP_ENV variable

// application/hooks/DisplayHook.php
class DisplayHook {
    public function captureOutput() {
        $this->CI =& get_instance();

        $output = $this->CI->output->get_output();

        if (getenv('APP_ENV') != 'testing') {
            echo $output;

            if(getenv('APP_ENV') != 'production') {
                if ($this->requestIsNotAjax()) {
                    echo sprintf(
                        '<img id="debug-overlay" src="%spublic/images/debug-overlay.png" /><style>#debug-overlay { position:fixed; top:0; left:0; z-index:-1; width:900px; opacity:0.25; }</style>',

    private function requestIsNotAjax()
        return  !(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == "XMLHttpRequest");

We set this value in a .htaccess file for Apache:

SetEnv APP_ENV development

At this point, we were writing a lot of tests because the existing application had none, and the student application was being redesigned.  There is a lot more to describe here, but I’ve got to parse through a lot of UI changes to find the useful bits.  I’m going post this as a Part One.  Part Two will include Pimple containers and bindings, controller actions to reach new code in /src, and other fun stuff.  Please feel free to post questions.  I would like to keep this as useful as possible.

Modernizing a CodeIgniter Application – The Problems

When I took my current job, I was assigned a specific project that supported the College’s distribution of Scholarship money to students.  This application had many errors and needed additional functionality.  Primarily, it needed to:

  1. handle students with more than one major
  2. reliably match students to Scholarship criteria
  3. allow the selection committee to give awards within in the application

This was a pretty tall order because the application was fairly large.  The bulk of functionality was within a single App model with functions that made SQL queries to the database.

Powered by WordPress & Theme by Anders Norén