Nice. I read that article and it points me to a  post I wrote in it.<div><br></div><div>So we've kind of already modularised monorails into separate services now, it's interesting that it feels like we could be reaching another complexity boundary.<span></span><br>
<br>On Thursday, 11 April 2013, Chris Parsons  wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><div>On 11 Apr 2013, at 00:00, Mark Burns <<a href="javascript:_e({}, 'cvml', 'markthedeveloper@gmail.com');" target="_blank">markthedeveloper@gmail.com</a>> wrote:</div>
<div><br><blockquote type="cite"><div style="font-family:Helvetica;font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:-webkit-auto;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">
Whilst passing enough info up to the controller or route level to execute plan_b would be possible it doesn't feel like a clean way to encapsulate things.</div></blockquote><div><br></div><div>Ok. The only other option might be to turn your domain model on its head and use something like a DCI or Hexagonal[1] approach, but that might be a step too far at this point - difficult to know if that would help without seeing your code + existing domain model.</div>
<br><blockquote type="cite"><div style="font-family:Helvetica;font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:-webkit-auto;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">
Introducing yet another moving part would increase our debugging complexity a step further though and highlight even more the need for improved cross-service logging, etc.</div></blockquote><div><br></div><div>That's true, but it sounds like your application has reach the level of complexity where queues makes sense, and that investment in good logging would be a good one to keep the cost of change curve as flat as possible. Ensuring you have some true end-to-end tests is also a good investment as the number of moving parts increases.</div>
<div><br></div><div>[1] <a href="http://blog.mattwynne.net/2012/04/09/hexagonal-rails-introduction/" target="_blank">http://blog.mattwynne.net/2012/04/09/hexagonal-rails-introduction/</a></div><br><blockquote type="cite">
<div style="font-family:Helvetica;font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;text-align:-webkit-auto;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">
<span></span><div>On Wednesday, 10 April 2013, Chris Parsons wrote:<br><blockquote style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word">
How about posting the call to the external API providers to a queue?<div><br></div><div>You can then return from the request with a good level of surety, and track the progress of the external API call, handling any failures and retrying as appropriate.</div>
<div><br></div><div>If a queue isn't an option, I tend to use different kinds of Exceptions:</div><div><br></div><div>* only catch very specific exceptions from the API calls</div><div>* re-raise my own Exception objects </div>
<div>* specifically catch my own Exception objects in the top level request code and handle as appropriate.</div><div><br></div><div>HTH,</div><div>Chris</div><div><br></div><div>--<br>Chris Parsons<br><a>chris.p@rsons.org</a><br>
<a href="http://twitter.com/chrismdp" target="_blank">http://twitter.com/chrismdp</a><br><a href="http://chrismdp.com/" target="_blank">http://chrismdp.com</a><br></div><div><br></div><div>BDD Kickstart London, May 22-24,<span> </span><a href="http://bddkickstart.com/dates#london" target="_blank">http://bddkickstart.com/dates#london</a></div>
<div><br><div><br><div><div>On 10 Apr 2013, at 21:01, Mark Burns <<a>markthedeveloper@gmail.com</a>> wrote:</div><br><blockquote type="cite"><div dir="ltr">I get the impression there is a pattern for doing this and probably someone on this list has some good input into it.<div>
<br></div><div>We've been thinking about how to handle failures in internal services, whilst integrating with third party services and trading off robustness and ability to debug complex requests and yet still notice actual genuine errors in our codebase. (e.g. avoiding things like 'try' and 'rescue nil' or 'rescue Exception')<div>
<br><div>Let's say we have three internal services A,B,C and some external API providers X,Y, Z.</div><div><br></div><div>Some object may be responsible for communicating with Z, but this object doesn't have access to the original incoming request.</div>
</div><div>Also it's absolutely critical that if this request to Z fails, the rest of the request can complete and the our external API user is hidden from the failure and some manual or separate automated process resolves the issue.</div>
<div><br><span style="font-family:arial,helvetica,sans-serif">To emphasise the criticality of such a system it would be where a user has paid for a service and one part of the fulfilment of the customer's purchase is achieved by an API call to an external provider Z. If this doesn't occur then we'd have angry customers and so we make sure the request is fulfilled by any means possible (manual if necessary), but still assure the customer we have fulfilled their order.</span><br>
</div><div><br></div><div>We've been toying with the idea of generating unique identifiers for our incoming requests and sending these in to all other internal services, then we'd be able to log these ids in all our log statements. We'd also ideally use these ids in communications to airbrake.</div>
</div><div><br></div><div>We could pretty easily create middleware that can generate the ids and send/receive them in headers to our other services, but the issue comes with having access to this info in our models. </div>
<div><br></div><div><font face="courier new, monospace">sinatra route/rails controller code</font></div><div><font face="courier new, monospace"> --   some long</font></div><div><font face="courier new, monospace"> --   stack frame</font></div>
<div><font face="courier new, monospace"> --  model code communicating with Z</font></div><div><font face="courier new, monospace"><br></font></div><div><font face="arial, helvetica, sans-serif">One solution that would get us to our controller/route code where we can access the request info would be throwing or raising</font></div>
<div><font face="arial, helvetica, sans-serif">exceptions, but this then prevents us continuing the request in the normal way and completing the required tasks after a call to Z fails. Also it's horrendous goto flow control.</font></div>
<div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">Other undesirable hacks would be sticking something on the thread itself, or a global variable.</font></div><div>
<font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">The other thing is to actually ensure we can pass down request info all the way through a stack, but this completely breaks single responsibility and is going to result in complex spa</font></div>
</div></blockquote></div></div></div></div></blockquote></div></div></blockquote></div><br><div><br></div><div><div>--<br>Chris Parsons<br><a href="javascript:_e({}, 'cvml', 'chris.p@rsons.org');" target="_blank">chris.p@rsons.org</a><br>
<a href="http://twitter.com/chrismdp" target="_blank">http://twitter.com/chrismdp</a><br><a href="http://chrismdp.com" target="_blank">http://chrismdp.com</a><br><br>BDD Kickstart London, May 22-24, <a href="http://bddkickstart.com/dates#london" target="_blank">http://bddkickstart.com/dates#london</a></div>
<div><br></div></div></div></blockquote></div>