Skip to content

Instantly share code, notes, and snippets.

@krfong916
Created February 28, 2019 07:01
Show Gist options
  • Save krfong916/5896c11d6a2e7bc0e661fb0ad3e7e4f6 to your computer and use it in GitHub Desktop.
Save krfong916/5896c11d6a2e7bc0e661fb0ad3e7e4f6 to your computer and use it in GitHub Desktop.
Two topics covered here, DDD and domain model boundary
public void SubmitOrder(OrderData data) {
var customer = GetCustomer(data.CustomerId);
var sendEmail = delegate { /* send email */ };
// call the domain model for the rest of the regular order submit logic customer.BecamePreferred -= sendEmail;
// to avoid leaking memory
customer.BecamePreferred += sendEmail;
}
public class CustomerBecamePreferredHandler : Handles<CustomerBecamePreferred> {
public void Handle(CustomerBecamePreferred args) {
// send email to args.Customer
}
}
public void SubmitOrder(OrderData data) {
// call the domain model for regular order submit logic
}
public class SalesComponent extends RequestHandler {
//We have to override this method to use the communication framework
public ResultCode AcceptRequest(String orderRequest) {
Json jsonOrderRequest = Json.Decode(orderRequest);
//…Do something with jsonOrderRequest… return ResultCode.OK;
}
//…Many more methods, some public, some not…
}
public class SalesComponentRequestHandler extends RequestHandler {
private SalesComponent _salesComponent;
public SalesComponentRequestHandler(SalesComponent salesComponent) {
_salesComponent = salesComponent;
}
//We have to override this method to use the communication framework
public ResultCode AcceptRequest(String orderRequestString) {
//sanity-check incoming external data
if (String.isNullOrEmpty(orderRequestString)) {
return ResultCode.ERROR;
}
Json jsonOrderRequest = Json.Decode(orderRequestString);
//additional sanity-check
if (jsonOrderRequest == null || jsonOrderRequest.isEmpty()) {
return ResultCode.ERROR;
}
//make a domain object from the data-carrier
OrderRequest orderRequest = OrderRequestFactory(jsonOrderRequest);
//let the domain object do its thing
if (_salesComponent.applyOrderRequestToCart(orderRequest)) {
return ResultCode.OK;
}
return ResultCode.ERROR;
}
}
/*It's more obvious that this class defines a boundary for your context
and that's all it does. We've also created an OrderRequestFactory
that takes a JSON request and produces a domain object OrderRequest,
so that our domain class, SalesComponent, does not have to know anything about JSON.
The SalesComponent class can now handle the domain-specific operation using plain-old-objects
from our context, and nothing else.*/
public class NativeWrapperAgent {
// This method is called when the user’s GPS location changes
public void onGpsLocationChanged(GpsPoint newLocation) {
String js = "document.getElementById('gpsView').innerHtml = '" + newLocation.LatLong().toString() + "';";
RunJS(js);
//make Javascript app execute js string
}
// …More stuff here
}
/*It's easy enough to see that the purpose of this method
is to inform the web view of changes in GPS location,
but the way it does that is somewhat, well, terrifying.
It constructs a JavaScript expression and tells the web component to execute it.
But isn't that what RunJS does? Isn't that how you have to do it in this environment?
Yes and no. The problem here isn't the mechanism; it's the domain-boundary blurring.
Recognize that this is also a domain-boundary method,
but it's an "outgoing" method: It informs the web component,
a JavaScript application, of something that happened on the mobile device.
The native wrapper is telling the JavaScript component what to do,
but it works and it's efficient, so what's so bad about it?
First, the native wrapper has robbed the JavaScript application
of the ability to make its own decisions. It should be up to the JavaScript application
to decide how to react to a change in the user's GPS location,
but here we have the native layer making that decision instead.
Second, the native wrapper has to know far too much about how the JavaScript app works.
So even though this is a boundary method, it fails to protect the boundary.
The solution in this case is to push all the foreign JavaScript knowledge out of
the native wrapper's context, by sending the JavaScript app simple notifications in a
neutral format. The JavaScript app can decide how and when to react to them. For example:*/
public class NativeWrapperAgent {
//This method is called when the user’s GPS location changes
public void onGpsLocationChanged(GpsPoint newLocation) {
String jsonGpsPoint = JsonEncoder.encode(newLocation);
String js = "handleEvent('" + jsonGpsPoint.toString() + "');";
RunJS(js);
//notify Javascript app of new event
}
//…More stuff here
}
/*Now the native wrapper only has to know the name of a single event-handling
function in the JavaScript app, instead of its entire internal structure,
and the JavaScript app decides what to do with each event. This strengthens
the boundary by minimizing the amount of foreign knowledge (JavaScript code)
required and using a neutral data carrier to transmit the event notifications.*/
// sources
// https://msdn.microsoft.com/en-us/magazine/ee236415.aspx#id0400034
// https://techbeacon.com/app-dev-testing/domain-driven-design-are-your-boundaries-leaking
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment