Check out the Latest Articles:
My Application Framework – Part Four – Brief SOA / Route Registration / Navigation

Brief SOA

Front end project can register routes and their navigation structures. The reason I like to register routes is due to the way of working I have undertaken since starting my new job. My new work use a Service Orientated Architecture. They have begun to break down their application into services made up of autonomous components. The components should be able to act independently but can work together by using messaging. Each component has its own entities, own entities, own front-end, own back-end etc.

It works by processing user input validating it, returning errors to the user or sending a message off to the bus. An action which is initiated by a component and processed by the components back-end is called a Command Message. Once processed by the message handler an Event Message is sent out to notify the other components.

Now I have an external message library which form the basis of these event messages allowing applications external to my service to listen to the queue and process any information they may need from certain events occurring. These external messages are extended by internal messages which are use by components inside the service and which may contain more sensitive information than what external sources may be allowed to see.

As in my case each component supplies its own nHibernate session which is built using the components fluent mappings. This should technically allow for your application to be distributed across many computers if demand requires it to be. It should allow it scale a lot more simply than the way traditional systems are built, by just deploying components or services to individual servers and setting them up to send messages to the relevant message queues.

As each component has its own entities and its own set of database tables it should be possible to “shard” the database and distribute it across many computers with no need for it to be in a cluster. Also I’m no expert on this but listening to conversations at work it should be possible to use load balancer’s to forward messages on to many computers running the same service or component. If a component is under strain you should just be able to add another server, setup a service host, listen to the message queues and away you go, you can now satisfy more demand on the popular parts of your system. Again don’t quote me on this, I’m young and still have a long way to go, but it sounds like a brilliant idea to me!

Routes

This basically forms my arguments for each front-end plug-in registering routes. As each autonomous component should be able to act independently, but each component has a slight coupling with the other components in its service. It should hide the concrete routes from any component or service which wishes to access what it offers. This way if you wanted to replace a component you could use the same registered routes and just point them to the new concrete routes in your new plug-in. I have managed to do this for web requests initialised by the user but not when I attempt to pull components into a host component using RenderAction. RenderAction does not seem to process the request through the routing engine so I get an issue in the Windsor Controller Factory. If anyone knows more about this process it would be good if you could get in contact with me and give me some advice on this one.

A Route Registration appears like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class RegisterRoutes : IRegisterRoutes
{
     public void Register(RouteCollection routes)
     {
          routes.MapRoute(
               "RoleCreate",
               "Role/Create",
                new { controller = "RoleManagement", action = "Create" }
          );

          routes.MapRoute(
              "RoleEdit",
              "Role/Edit/{id}",
               new { controller = "RoleManagement", action = "Edit", id = "" }
          );

          routes.MapRoute(
              "RoleRename",
              "Role/Rename/{id}/{name}",
               new { controller = "RoleManagement", action = "Edit", id = "", name = "" }
          );

          routes.MapRoute(
              "RoleDelete",
              "Role/Delete/{id}",
               new { controller = "RoleManagement", action = "Delete", id = "" }
          );
     }
}

A class simply implements the IRegisterRoutes interface and register routes as if it would in the Global.asax. Then on start-up these classes are resolved from Windsor and executed passing in the RouteTable.

Navigation

Front end libraries can also register their navigation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class RegisterNavigation : IRegisterNavigation
{
     public NavigationNode Path
     {
          get
          {
               NavigationNode host = new NavigationNode
               {
                   Action = "Index",
                   Controller = "UserHost",
                   Name = "User System"
               };

               NavigationNode roles = new NavigationNode
               {
                   Controller = "Roles",
                   Name = "Role Management"
               };

               host.Children.Add(roles);

               return host;
          }
     }

     public string[] Roles
     {
          get { return new[] { "Creator", "Admin", "UserAccess" }; }
     }

     public bool RequireAllRoles
     {
          get { return false; }
     }
}

This defines a tree structure of Navigation Nodes which include the details to create the navigation links, and the security information to access the part of the system.

All of these navigation registrations are passed into a class which creates a navigation tree on start-up. This class is used by the navigation controller to render the navigation for each user. Upon request the class loops over each registration and adds it to a navigation data structure if the user is privileged enough to access the part of the system the registration describes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public DataStructures.Navigation GetNavigation(ThreeBytesPrincipal principal)
{
     var result = new DataStructures.Navigation();

     foreach (var nav in container.ResolveAll<IRegisterNavigation>().OrderBy(x => x.Path.Name))
     {
          if (nav.Roles.Length > 0)
          {
               if (principal != null)
               {
                    if (nav.RequireAllRoles)
                    {
                         if (!principal.IsInAnyRoles(nav.Roles))
                              continue;
                    }
                    else
                    {
                         if (!principal.IsInAnyRoles(nav.Roles))
                             continue;
                    }

                    result.Add(nav);
               }
          }
          else
          {
               result.Add(nav);
          }
     }

     return result;
}

The Navigation Data Structure recursively loops over the path supplied by the registration adding it to a Navigation Nodes list, merging nodes of the same path until they differ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Navigation
{
     public IList<NavigationNode> NavigationNodes { get; set; }

     public Navigation()
     {
          NavigationNodes = new List<NavigationNode>();
     }

     public void Add(IRegisterNavigation navigation)
     {
          AddInternal(navigation.Path);
     }

     private void AddInternal(NavigationNode path)
     {
          AddRecursive(NavigationNodes, path);
     }

     private void AddRecursive(IList<NavigationNode> navigationNodes, NavigationNode toAdd)
     {
          if (navigationNodes.Contains(toAdd))
          {
               foreach (var node in toAdd.Children)
               {
                   AddRecursive(navigationNodes.Single(x => x.Name == toAdd.Name).Children, node);
               }
          }
          else
               navigationNodes.Add(toAdd);
     }
}

I then have a HTML Helper which outputs the relevant HTML to complement the structure built by this class.



  1. It‘s quite in here! Why not leave a response?