Event Driven Code
As part of my recent work rebuilding the payment system for the Build Brighton member system (BBMS) I have started using events to loosely couple my code. Within Laravel events are a basic pubsub system, one part of the codebase fires an event with some data and another part listens for it and acting on that data.
I had used some events in Laravel before but its a design pattern I had steered away from, at least for critical pieces of code. When you viewing a controller or some other part of the code and you come across an Event::fire()
statement there is no easy way to see what that does. You're not sure if anything is listening to it or if the events were even spelled correctly, unlike a normal method call it's a dead end for the IDE and the viewer.
Yes, you can go to the event handlers folder and take a look, or to the service provider or global file where everything gets subscribed but it's an annoying step and one that I didn't want to have to take.
My view on the subject hasn't changed but for me the alternative started to look a lot worse!
Handling the events
Going back to the BBMS, I started to look at what happens when we receive notification of a subscription payment, in this situation a couple of things need to occur. Firstly the payment gets recorded or updated depending on the type of notification, the related subscription charge gets located and updated and finally the user record will get updated with a new status or expiry date. It's a lot to manage but not terrible.
The BBMS receives payment notifications from various sources; GoCardless for two types of payment, PayPal, bank statement uploads and manual payments. After making changes to the system to work from the subscription charge records I decided the current system wasn't manageable.
The approach I am now working with is to focus on what has just occurred and let other systems tie into this. When a payment comes in I will now record this with a single method call to the payment repo, the method will save this data, fire an event and return.
Examples
When a payment update comes in from GoCardless the following is called.
$this->paymentRepository->markPaymentPaid($existingPayment->id, $paymentDate);
The payment is then updated and an event fired
Event::fire('payment.paid', array($payment->user_id, $paymentId, $payment->reason, $payment->reference, $paidDate))
The payment event handler looks at the type of payment and if its a subscription payment updates the subscription charge.
$this->subscriptionChargeRepository->markChargeAsPaid($reference, $paymentDate);
We then continue further down the rabbit hole with another event being fired by the above method!
Event::fire('sub-charge.paid', array($chargeId, $subCharge->user_id, $subCharge->charge_date, $subCharge->amount));
The subscription charge event listener finally ends by updating the users expiry date.
In conclusion
This process is a little long but everything is separated out and only responsible for its own thing, the events now play a key role in the system so its unlikely I am going to forget they are there.
A side benefit of this is that by separating actions with these events I am making it easy to add a queue to some or all of these processes. Each event carries with it the information it needs to complete its task, perfect for placing onto a queue.