-
Notifications
You must be signed in to change notification settings - Fork 3
Dependency injection
We want to develop each service independently, so with that comes transparent dependency integration. Each service can for example use the same message bus technology, but configured differently, possible with different lifetime. In this project CastleWindsor container is used, but you can use any other in your project.
Solution to this comes with looking at responsibilities of main web application and each service. Main app provides configuration, publishes incoming requests and gathers responses, provides public api and navigates user through it. It is also great place for cross cutting concerns like authentication, authorization, logging, etc. Rendering part of main app only provides grids and templates that render content to a user. Services on the other hand provide all needed data and behavior on a page and actually handle incoming requests, each of them in its way. So to sum up each service is different from any other and all services have different responsiblities from main application, what is more each controller in whole application is unique - as there are no common controllers - and it's a controller that is starting point of resolving dependency graph. As a result each component can have its own dependency container.
For whole application to work we have to integrate different containers, because resolving dependency graph for each request usually starts with creating controller instance and because each container is responsible for creating controllers from its service, they all must be integrated at this level. We also need to manage lifetime of every dependency, so each component must be integrated also at this level. For both of these requirements we can expect in main application's container that each service will deliver for example implementation of IControllerActivator - that will be responsible for creating controller and resolving its dependency graph, as there are no common controllers - and implementation of IControllerFactory for releasing each controller and managing lifetime of its dependencies.
Example of integrating containers for creating controllers:
public class WindsorControllerActivatorDecorator : IControllerActivator
{
private readonly IKernel _kernel;
private readonly IEnumerable<IControllerActivator> _activators;
public WindsorControllerActivatorDecorator(IKernel kernel, IEnumerable<IControllerActivator> activators)
{
_kernel = kernel;
_activators = activators;
}
public IController Create(RequestContext requestContext, Type controllerType)
{
if (_kernel.HasComponent(controllerType))
return _kernel.Resolve(controllerType) as IController;
foreach (var activator in _activators)
{
var controller = activator.Create(requestContext, controllerType); //only one container can create given type
if (controller != null)
return controller;
}
return null;
}
}
Example of integrating containers for releasing controllers:
public class WindsorControllerFactoryDecorator : DefaultControllerFactory
{
private readonly IKernel _kernel;
private readonly IEnumerable<IControllerFactory> _factories;
public WindsorControllerFactoryDecorator(IKernel kernel, IEnumerable<IControllerFactory> factories)
{
_kernel = kernel;
_factories = factories;
}
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var c = base.CreateController(requestContext, controllerName);
return c;
}
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller);
foreach (var factory in _factories) //only one container can release given controller
factory.ReleaseController(controller);
}
}
There's now issue how to deliver this implementations to main app's container. For doing this we can define in web.config of main app to load each service's web application when main application starts:
<system.web>
<compilation debug="true" targetFramework="4.5">
<assemblies>
<add assembly="CompositeUI.OneService.Web" /> //load this assembly on app start
<add assembly="CompositeUI.SecondService.Web" />
</assemblies>
</compilation>
<httpRuntime targetFramework="4.5" />
</system.web>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin\OneService;bin\SecondService" /> //look for assemblies also in these folders
</assemblyBinding>
Now when main app's container is created we want to tell services apps to configure themselves and provide needed infrastrucre to main container. In order of doing so, we define abstract ServiceWindsorInstaller class, that will implement standard IWindsorInstaller interface, so we can extract instance of this class from each service app and execute it as normal dependency installer for main app container. Execution of these objects will be signal for each executing service, that app has started and it's time to configure its own container and provide configuration to main one:
public abstract class ServiceWindsorInstaller : IWindsorInstaller
{
protected readonly string _binPath; //bin path of main application
public ServiceWindsorInstaller(string binPath)
{
_binPath = binPath;
}
public abstract void Install(IWindsorContainer container, IConfigurationStore store);
}
public class WindsorBootstraper : ServiceWindsorInstaller //private service container
{
private static volatile IWindsorContainer _container;
private static readonly object _lockObject = new object();
internal static IWindsorContainer Container { get { return _container; } }
public WindsorBootstraper(string binPath) : base(binPath) { }
public override void Install(IWindsorContainer container, IConfigurationStore store) //signal that app has started
{
var path = GetProjectPath();
ConfigureContainer(path); //configure service container
container.Install(new ExternalWindsorInstaller(path, _container)); //provide infrastructure needed for main app
}
Infrastructure needed in main app for integrating service with others, apart from resolving and releasing controllers, contains view engine and request handler. All other components are optional - as for example IApplicationConfiguration that have two methods to call when app starts and when it is closed in order to for instance release service container.
As this solution doesn't rely on features of specific container, you can use it with any dependency container you like.
-
Welcome
1.1 Project overview
1.2 Features -
Introduction
2.1 UI composition example
2.2 Service separation
2.3 Service communication
2.4 UI composition goals
2.5 Clues how to start
2.6 Potencial problems -
Identifying widgets
3.1 Naming widgets
3.2 Widgets format
3.3 Amount of widgets
3.4 Widgets and service boundaries
3.5 Widgets and caching
3.6 Grid for widgets -
Delivering view models
4.1 Publishing request
4.2 Internal routing
4.3 Data in view models
4.4 Gathering view models -
Rendering view models
5.1 External routing
5.2 Including route values
5.3 Finiding physical view files
5.4 Template views - Getting data from request
- Sharing resources
- Service api
-
Transactions
9.1 One transaction
9.2 Multiple transactions
9.3 Transaction performance - Dependency injection
- Public api changes
- [Tables] (https://github.com/padzikm/CompositeUI/wiki/Tables)
12.1 Table order
12.2 Server-side rendering
12.3 Client-side rendering
12.4 Nested tables -
Cross-service validation
13.1 Server-side validation
13.2 Client-side validation -
CRUD
14.1 Service private data
14.2 Create
14.3 Update
14.4 Delete
14.5 Preview -
Caching
15.1 View models
15.2 Widgets
15.3 Pages - Optimizing network calls
- Scalling
- Client-side communication
- Deployment
- Starting new project