Sadek Drobi’s Blog

June 10, 2009

DCI in Real World: Domain Context and Interaction with Scala in a Real World Project

Those that follow my twitter @sadache , me @infoQ or my blog have certainly already noticed that I am quite interested in Scala on languages’ axis and in Domain Context Interaction DCI pattern on architecture axis. I always search new ways for delivering quality code which is modular and concise. Modularity offers the opportunity to think about the problem in parts, which is typical of the way brains work, whereas conciseness makes use of imaginary system (reading code blocks like images).

Recently, I’ve been working on a Web Api system where, thanks to support of @jeanlaurent http://morlhon.net/blog/, I used Scala applying DCI architecture in a real world project. This post is about reporting benefits of using this approach. Other posts will follow that will be more focused on the use of Scala and Functional Programming in that project. Code included is a bit simplified and parts of the system that are not of interest are omitted.

The concerned part of the system is an API for logging a Web API usage and using it for showing interesting statistics to users and for implementing a Web 2.0 business model authorization.

The story starts with an API key passed as a parameter in the requested URI something like product/1234?api_key=AFFSEFFVFE2344

I create an object that represents the API user containing the key: (did I tell I like one liners?)

image 

This is a class of an object that holds the apiKey, now I need a method that I will call to log the use of product/123 for user represented by the apiKey AFFSEFFVFE2344. What could be quite convenient and OOP-iesh is to call a record method on the apiKey instance, something like apiUser.record(…) . The problem with this, as explained in the original DCI article, is that adding these methods to the domain objects adds noise in other contexts for which recording is not relevant such as authorization and statistics, as we will see later. I would rather use here the DCI’s role concept (or context). Doing so I would say (apiUser in recording role).record(…) . This pseudo code tends to be very close to mixins implemented in Scala language and will look like following in real world Scala code:

image 

Here the ApiUser object doesn’t contain a record method by default and so it can be shared in different contexts. In other words we kept our domain objects pure yet not scarifying OOP convenience. Now the Recorder is a trait that minds only business in the context of recording. The definition looks like the following:

image 

Another view of the system is the end user’s statistics. Statistics should be represented in a meaningful way to the user and the user can’t care less about how the recording is done. So, it is quite desirable not to have the noise of recording when talking about the statistics view context. Here is how the call looks like in our example in Scala:

image

Again what we are doing here looks merely like saying: I want to use the ApiUser in a statistics context and I choose to call method getRequestsPerMonthForLast12Months. I guess the convenience brought by using this approach is obvious. This is how the Statistics trait looks like:

image

Note that as in the Recording trait, the ApiUser object is not passed as an explicit parameter but rather as an implicit receiver parameter (something like this for an object). To access it we use Self that refers to it.

The third view of the system is what allows us to provide an authorization mechanism based on user’s activities for a business plan they choose (be it a quota per day, per month or other interesting combinations). It could be quite convenient to have the call looking like apiUser.isAuthorized . We get pretty close with DCI without polluting our domain object with a specific context logic:

image

Like the previous, the Authorizer trait looks straightforward :

image

Now that we looked at the three views of our system introducing three examples of “Context” from DCI pattern we can go beyond that to discover how we use nested contexts (or roles).

Looking at the Authorizer trait, the ApiUser referred to by Self, doesn’t tell us much about how many requests the user did today or this month. And for authorizing user one needs a richer ApiUser that can give the necessary information that live themselves in a file or a database. But once again, we don’t want to pollute our domain object with Data Access code that varies on a different axis than the domain object itself.  Introducing a nested context/role that adds the Data Access logic would pretty convenient here:

image

Here Self is a richer ApiUser that carries Requests Data Access logic defined in the RequestsDataAccessor trait:

image  

And the implementation of the Authorizer trait defined in terms of ApiUser with RequestsDataAccessor:

image

In the same way we implement the Statistics and Recorder traits using ApiUser with RequestsDataAccessor:

image

image

If you are not familiar with generics ( getRequestsPer[Day] ) you can ignore them since they are not central to the subject of this post and simply use their readability to understand the purpose of the method. The use of generics in this project will be the subject of a future post.

Encapsulation Test!

Now in the first version of the system, the ApiKey was passed in clear in the service URI. This is not very secured as it is quite easy to get the key and use it for using the API. We might prefer to encrypt the key in some way. This fortunately changes nothing for any of our traits implementations since the decryption of the key is the responsiblity of the ApiUSer object and all what our traits need is to get the concerned ApiKey from that object which can be easily done with pattern matching. Same thing if we want to replace database logging with file system in RequestsDataAccessor. Modularity and separation of concerns are apparent in this DCI architecture implementation.

 

Conclusion

This is a refreshing approach to OOP. The Domain Context Interaction pattern allowed us to restore interesting properties of real OOP approach yet not sacrificing modularity and separation of concerns. This simple view of “an object in several contexts” helps quite much in thinking explicitly about and understanding context boundaries. Note that an object(instance) is not defined by a single class but each instance curries the necessary context. This is essential to OOP which is NOT COP (for Class Oriented Programming ;) )

 

Feel free to ask questions and/or comment about this implementation. Refer to original papers for more in-depth explanation of the pattern.

Thanks to JetBrains’ IDEA and the Scala plug-in that helped me programming in Scala.

 

5 Comments »

  1. Note, the use of self type here is unnecessary if you already extend target class. Use either this:

    trait RequestAccessor extends ApiUser {

    }

    or

    trait RequestAccessor { self: ApiUser =>
    }

    but not both. The difference between those two versions are subtle. See for instance http://www.drmaciver.com/2009/04/scala-trivia-of-the-day-traits-can-extend-classes/

    Comment by Joni — June 11, 2009 @ 7:26 am

  2. No need to use self types if the type extends the self types.

    Comment by Jesper Nordenberg — June 16, 2009 @ 9:03 am

  3. I didn’t know that. Thanks!

    Comment by Sadache — June 28, 2009 @ 10:47 pm

  4. Very interesting article. Would it be possible for you to post the complete code or whatever code is available? Thanks.

    Comment by Chris Leong — July 26, 2009 @ 1:45 pm

  5. Very interesting. Practically begs for some implicit conversions, no? Or would that ruin the sensation of explicitly invoking a role?

    Comment by Aaron Harnly — October 18, 2009 @ 12:04 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress