-
Notifications
You must be signed in to change notification settings - Fork 0
Home
The Wiki is a work-in-progress so bare with us. The audience we are targeting is high school students in their final year or at least with a term of programming.
#5000 feet#
From a bird's-eye view the structure of this repository is twofold. First there is the division between the code under src/Prenotes.Services
and what tests the code under test/Prenotes.Services.Test
. These are two separate C# projects with the test project linking in the source project through a file reference (i.e. see the Prenotes.Serivces.Test.csproj
file). The layout in the source project is mirrored by the test project:
/Actions
/Stores
/Things
(Other maps that are decorator which we have written about a little far down)
The flow is that things are simple (but immutable) classes that form the basis of our data model. Actions (synonymous with services) define how we interact with things. Stores are pure functions that are used by actions (and decorators) to grab and store things in our database.
#Design patterns#
There are just 2 patterns that have pushed the design of Prenotes.Services
into a certain direction. Below we cover the decorator and builder patterns.
##Decorator# Prenotes has a bunch of actions (i.e. services) that create, edit, find and delete things. The things are simple classes that map to nodes in the database (i.e. our model). Actions are pretty dumb. As long as we provide the right parameters anybody could run any action without any checks. Before letting an action operate we might want to validate a parameter. Each action accepts one or more things as parameters. Without validation we might accidentally let a bad email slip into the database. Emails are unique identifiers for users. It is important that the email property is validated. In fact, there are an unlimited number of before and after checks that we might want to perform.
Rather than bake all of that logic into an action we want a solution that is flexible. The decorator pattern is one solution. We wrap an action in an infinite number of decorators. Each decorator implements the contract for the action (i.e. the interface). For example, the ICaretakerSerivce
is the interface for the CaretakerService
action. The interface declares all of the methods important for dealing with caretakers (i.e. legal guardians). There is a Confirm
method which accepts Caretaker
object and a code as parameters then returns a Caretaker
object. The signature of the method is:
Caretaker Confirm (Caretaker c, int code) {...}
Both the concrete CaretakerService
and the abstract CaretakerDecorator
have an implementation for the Confirm
method. The decorator's method will perform logic prior to calling the action's Confirm
method. It can even do stuff after the actions's method has returned:
public abstract class CaretakerDecorator {
...
public virtual Caretaker Confirm(Caretaker obj, int code) {
return srv.Confirm(obj, code);
}
}
The srv property holding the service (action) is read-only and set by the decorator's constructor:
public abstract class CaretakerDecorator {
protected readonly ICaretakerService srv;
public CaretakerDecorator(ICaretakerService srv) {
this.srv = srv;
}
...
}
The decorator wraps the underlying action or can wrap another decorator. This way we can first stack a decorator over the action then keep stacking decorators over decorators adding functionality. Remember that the decorator implements the ICaretakerService
contract. The last decorator just any other decorator in the Russian doll of decorator around an action is still implementing the contract of the action.
Decorators are fairly straightforward (e.g. Validation). The downside is the amount of boiler plate. Also, somebody has to stack the decorators on our service and possibly configure individual decorators. This is where the builder pattern comes into play.
##Builder pattern##
The builder pattern will help automatically piece together a service and stuff it inside a bunch of decorators. A builder hides the construction of an object. The ServiceBuilder
class is static. It builds our services. For example, the CaretakerService
could be constructed like:
public static ICaretakerService CaretakerService(string url, string user, string password) {
var driver = GraphDatabase.Driver(
url,
AuthTokens.Basic(user, password)
);
// Just one validator
return new CaretakerValidator(
new CaretakerService(driver)
);
}
We accept configuration information like how to access the database and fire back an interface. Since our decorators adhere to the service's interface their involvement is transparent. Obviously, the example above is contrived. We will have more configuration information and more decorators.