App Server Revisited

April 26, 2014

 

I haven’t worked on the app server in a couple months. I got bogged down with other things, including Assassin’s Creed 4.

I started to revisit it about a week ago, and had to make some changes. Primarily, I’m yanking WCF. WCF was used for many things. It was used to communicate across appdomains via the NamedPipeBinding (rather than MarshalByRef, in anticipation of process isolation rather than app domain isolation. Also, a core feature was the simple hosting of Wcf Services. You simply put an attribute on a class, and it starts up as a Wcf service automatically.

I used WCF because I know it, and I already had a lot of existing code that I just had to slap around a little bit to start using. But the days of Wcf are behind us. The era of Web Api and Signal R is upon us. (Incidentally, it was already using SignalR.)

My intent was to rip it out with extreme prejudice. Everything was abstracted pretty well, so it would’ve just been a matter of writing some new implementations and plugging them in. Nice. I started to do that, then aborted.

There were a few other things that I weren’t completely happy with. Everything in it is driven by the command pattern, which I like. It gives you full control of the order that things happen. And I like the way it’s implemented. But, there’s also a process bus spanning all app domains and (although not implemented yet) a hub. That is done a bit different. There’s a distinction between the server commands and the bus messages even though they accomplish similar things in different places.

So, I gutted that too. I changed it so that everything is done via the bus, including the server commands. Absolutely everything is done via the bus. That was the big one. I’m still working through it.

I’ve also tightened up and formalized how to issue commands. Previously, bus commands were strings because one of the primary entry points of them is the admin console. It’s built to send objects, and string is an object. So, a lot of commands were strings loaded into a generic COMMAND object. Then subscribers would parse out the string and respond with an object. (Actually, the command object would break it up into it’s parts, then the subscribers would pick through the parts.) That can be better, and now it is.

The trick to that is: if someone is typing on the command line, how do you convert that to an object? I do it as follows: if you type “appstop mydemoapp”, then it instantiates an object called AppStopRequest and sets the ApplicationName property to “mydemoapp”.

If you type in “appstop someserver someapplication”, it maps some server to the ServerName property and SomeApplication to the ApplicationName property. The mappings are handled by attributes on the class. Here’s an example from a test class.

        [CommandText("{a}")]
        [CommandText("{a} {b}")]
        [CommandText("{a} {b} {c}")]
        public class TestRequest : Command
        {
            public string A { get; set; }

            public string B { get; set; }

            public string C { get; set; }
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

When you enter a command, it determines the number of parameters you typed, then finds the attribute that has the same number of attributes. Then it uses that to populate the properties. It’s really simple right now. That will probably evolve.

COMMAND: test ThisIsA ThisIsB

One final significant issue: I spend an absurd amount of time, as previously blogged, building a setup api. It’s a fluent interface that allows you to specify exactly what you want done, and swap out implementations, set options, etc. I probably spent more time on that than anything else. It works really well. But, after being away from it for a few months, coming back to it and looking at the code, it was cumbersome to work with. Using it is fine, but writing it kind of sucks. I wrote it and didn’t want to write more, so I can’t imagine anyone else would want to deal with it despite how nifty the resulting API is.

I discarded the tens of hours of work I did on that and started fresh. When I wrote the first one, I started by basing it on an IOC container and providing extension methods to set it up. That didn’t work on my first attempt. I don’t recall all of the reasons why, but it’s working better the second time. Part of it is lessons learned, and part of it is some fundamental changes. The previously covered command changes helped. Also, I relaxed a couple rules that I was trying to work around in order to enforce.

Rule #1 – Bent. I didn’t bind to a particular container. I like Unity. Other people like other things. I want theoretical future users of this to use another container if they want. To keep that completely separated, I need a project with a reference to unity so that unity isn’t bound to the core projects. You add it as needed. I’m not doing that anymore. You can still swap out the container, but the core projects have a reference to unity. These days, it’s only one DLL. If you don’t use it, then fine. But, it’s there and allows everything to work out of the box quite simply. I was doing the same thing with SignalR. I haven’t gotten to that part yet in the new version, but I may do the same thing. (Unfortunately, I think that would result in several references. We’ll see what happens when I get there.)

Rule #2 – Discarded. I was adamant about not using the Service Locator anti-pattern no matter how much easier it would make things. I ended up with a lot of Func<T>s in my constructors, and jumping through hoops to get what I needed, and keeping them current. Especially collections of things that may change. I am no longer practicing that rule. The core projects use service locator where it helps. And, behind the scenes, it helps a lot. I will not use it in pluggable components, etc, but I am using it in support of them. This has really simplified a lot of things.

That said, nothing is coded to IUnityContainer in particular. I have a wrapper called IDependencyResolver, and a unity implementation. All of the extension methods, etc, are coded to IDependencyResolver, so it will all work on any container.

Microsoft provides a Unity ServiceLocator DLL that provides an abstraction. I used that in a project once and then ripped it out later. I didn’t use it here for the following reasons:

  • It has “Service Locator” right in the name. I don’t want to be blatant about it’s presence.
  • It would require a reference to that assembly in a few places. Writing my own in an assembly that is already everywhere eliminates one more third party assembly.
  • My implementation has more in it than IServiceLocator. I could’ve led with this as it renders the other 2 bullets irrelevant. One of the core classes that I use is called TypeCache, which I must have blogged about before. It crawls the appdomain once and collects information about types and attributes you are interested in. And it allows you to retrieve those types by a friendly name. You can specify the friendly name by an attribute on the class. If not present, it uses the class name. The IDependencyResolver interface is in support of both the IOC and the type cache.

The interface has a couple extra things on it that I will need to rethink a bit, but I’m not getting hung up on it yet.

In conclusion: I’m working on the app server again, at least for the moment. I’ll stop again when I get bored again, or too busy. As with most things I do, it’s more about learning and proving to myself better ways of doing this. If I happen to finish it at some point, then fantastic.

I absolutely did not restart this time. But, I created a new solution, and I’m porting things over one at a time. As with every new version of a piece of software, it’s better than the previous. Let’s see if it holds my attention this time.