In this post I’d like to talk about composition and inheritance on practical example.
First of all I’d suggest to read about Repository pattern of EAA.
Lets consolidate all previous materials by implementing UserPermissions service. I’ve posted base concept of this service here, but domain model has been slightly changed during actual implementation.
Here is the Use-Case sketch of our service:
Service should provide methods to grant user permissions (using jabberID and chat room name) revoke and modify them.
If there is no such a user in database having given jabberID, new user should be created and mapped to database (e.g. persisted). The same action should be performed with new chat room.
Lets assume that database tables contain this information:
As you can see we have got three users – Steeve, Mark and John sitting in firstname.lastname@example.org
Personal information is stored in users table. Information about chat rooms is stored inside rooms table. Finally permissions table is used to link all this information together with acess level for each user.
Note that Steeve and Mark both have multiple jabber accounts and use them to talk in the same chat room. This situation is perfectly handled by our db schema.
Here is the sequence of actions that should be performed when new user email@example.com joins chat room firstname.lastname@example.org:
- New user with empty personal information should be created and mapped into users table
- No new rooms should be created/mapped
- User should be assigned zero access level since he is new user, we know nothing about him etc
- New record should be inserted into permissions table according to steps (1)-(3)
So generally we should invoke three database mappers to create and register new user (one mapper per database table).
Access level lookup
Our service should allow client to get access level value, using user’s jabberID and chat room name. If you will review our use case sketch again you’ll notice that this use is specially marked in red.
This is time critical operation. From this point of design all plugins will use this operation in order to check if user has enough rights to execute plugin content (e.g. command, action etc).
In this situation we will use HashMap with complex key. Our service will be responsible for building this HashMap using database entities and domain objects and manage it during lifetime. When new user is added/deleted or access level is changed service should edit HashMap as well.
Here is how we create service:
Repository repo = new Repository(db); UserPermissionsService service = new UserPermissionsService(repo); //dowork with service //...
As you can see from class sketch our permissions service stores reference to Repository class. In order to create new user/room or insert permissions record permissions service accesses Repository. Using this design we’ve isolated service from database mappers and all other database stuff.
Repository on the other hand is responsible for caching users and rooms using Identity Maps. It is also used as OR-mapper (provides getPermissions() method) for getting UserPermissions records list. This list is used by our permissions service in order to construct permissions lookup HashMap.