Hello! I want to give an example of layers-architecture and the role of the Unity container in it. Otherwise, the people write about the container itself, but they cannot write how to use it properly. Let me try.
I'll start very far away, sorry right away. Simply, in order to explain something useful, you need to come up with something really useful.
(Not very) life example
Suppose you are a cool developer and you have two programmer friends: Kolyan and Tolyan. Kolyan and Tolyan are all good, but experience is not enough, the quality of their code, as a result, suffers, but you are friends with them and sometimes even do a project together.
')
Now - suddenly, out of nowhere does the customer from IBBC call you and loudly shout:
- Volodya! We urgently need to get an application that will display items on the screen! Deadline week. Money - a million!
And hangs up.
Since any design begins with the design, we will design.
Layers and black boxes
We will build the system on layers.
The definition of the software layer from the Microsoft Architecture Application Guide is as follows (in a somewhat loose translation of me, sorry):
A layer is a logical grouping of software components that make up an application or service.In other words, in the “layered architecture”, that same heap of software components that make up an application is divided into several logically related subsets. Everyone knows the classics of the genre - the UI layer, the business logic layer, the data layer.
Judging by the customer, we need to show something in the user interface and take data from some source. Therefore, the system will consist of two layers - a UI-layer, a Data-layer, as well as a driver (which connects the layers + main ()).
Agreed that the UI will be written by Kolyan, and the data layer will be written by Tolyan. You will carry out coordinating activities and write the program driver plus cross-cutting code.
Black boxes
A layer is a software module (an assembly or assemblies with one main in .net). This module can be implemented as a black or white box.
- The black box means that you know the interface component of the layer and can only operate on the functionality of the box through it.
- A white box means that all control entities of a program component are visible by other components that are aware of the existence of the box.
In other words, you do not know what is in the black box, but you have a control panel (interfaces) to it.
In the white box you are the master - what you want, you are doing with its filling. It is transparent and all the insides are visible.
In our case, designing the layers using the black box method is kosher, which is why:
- You achieve conceptual integrity and encapsulation. Having developed a single mechanism for accessing a layer through its interface, you will acquire semantic completeness (that is, a limb), which allows you to operate only with box entities.
- Encapsulation and (very importantly) ignorance of how the concrete entities of the layer are constructed allow you to focus on a more general problem, removing the care of unnecessary subtleties from your head.
- In the end, the best minds from programming tell us about these postulates, so let's hear the clever uncles :)
Container and its role
From all of the above, it turns out that you need to organize a software module in such a way that it:
- Implemented his assigned contracts
- Hid the entire implementation of specific entities.
Since we encapsulate all the implementations in the box, we cannot directly access them and work with them in the usual way. We need to somehow link the contract of the box and its implementation, save it somewhere, so that later other participants in the process can use them. For this you need a container. In the degenerate case itself, it is the usual “interface-specific type” dictionary. In a more complicated case, like, for example, with Unity, it’s also a dependency injector and other blackjack sluts.
At the time of launching the application, you need to dip the container into the layer and allow the layer to register its implementations for the contracts it is bound to. After all the implementations are assembled, you can already write the control code, not worrying who we have xy.
All this you planned out in your head (what a fine fellow), now we proceed to the development stage.
Code
Need a contract assembly. They can be made several - on each layer. But I will make one common, for simplicity.
Tolyan makes us access to data. Let its layer implement this:
public interface IDataService
{
object GetData();
}
* This source code was highlighted with Source Code Highlighter .
And Kolyan, respectively, got UI:
public interface IDataView
{
void ShowData();
}
public interface IViewFactory
{
IDataView Create(Type viewType);
}
* This source code was highlighted with Source Code Highlighter .
A software module must contain a single public class so that we can put our hands in the container with it and scoop up a handful of implementations:
public interface IModule
{
void Configure(IUnityContainer container);
}
* This source code was highlighted with Source Code Highlighter .
All contracts are defined. Now we need to put everything together in the control application:
class ShellApplication
{
public ShellApplication()
{
_container = new UnityContainer();
}
public void Run()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false );
_container.RegisterInstance<IUnityContainer>(_container);
_container.Resolve<Layers.Data.Module>().Configure(_container);
_container.Resolve<Layers.UI.Module>().Configure(_container);
MainForm form = (MainForm)_container.Resolve( typeof (MainForm));
Application.Run(form);
}
private IUnityContainer _container;
}
* This source code was highlighted with Source Code Highlighter .
partial class MainForm : Form
{
public MainForm(IViewFactory factory)
{
InitializeComponent();
_factory = factory;
}
protected override void OnLoad( EventArgs e)
{
base .OnLoad(e);
IDataView view = _factory.Create( typeof (IDataView));
Controls.Add((Control)view);
view.ShowData();
}
private IViewFactory _factory;
}
* This source code was highlighted with Source Code Highlighter .
With your part all.
Now we need to force Kolyan and Tolyan to write the necessary code. With proper skill, you can write all the parts together in parallel. Thus, the charm of development is in its chic scalability. If you plan everything correctly, the development of layers of modules starts quickly and quickly, without being distracted by other parts.
Iteration is also supported - you can write in a descending manner. First you need to write the control code, hanging on it "fake layers" and changing the contracts under the requirements in the iterations. After the control code is debugged and fixed, it remains to sweep the corners - to realize the boxes.
Kolyan, Tolyan and I got this:

See all around the envelopes. Layers stuck out only their tail - the class of the module.
And to the peasant from IBBC we made his application and grabbed a lot of money. Tolyan and Kolyan bought dozens, and I - prior.
Stand still. I almost forgot. You can tell me “Ha-ha-ha-ha !!! Sucker! Sucker! ”, About the same as Henry Rollins did at the end of the fourth minute of his very good
song . How did I hide all the implementations, and unit testing?!?!
Well, I do this:
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo( "Services.Tests, PublicKey=00240000048000009[skipped]1fbcb51f56dfcc8db5320774f354553ad" )]
* This source code was highlighted with Source Code Highlighter .
No need to explain? :)
Features, conclusions, notes, bonuses and drawbacks
Sory that I do an example on Windows Forms. I would like to tell you how to arrange the architecture of an ASP.NET MVC application in this way, but Comrade
XaocCPS will beat me for it. I would love to fight, but he is faster and faster than me. Leave this topic to him.
1) The control assembly should have references to all layers and assembly contracts. Otherwise, it will not be possible to collect implementations.
2) Kolyan had to use the Tolyan data service (according to the paradigm, the UI layer depends on the data layer):
partial class DataView : UserControl, IDataView
{
public DataView(IDataService data)
{
InitializeComponent();
_dataService = data;
listBox1.DataSource = _source;
}
#region IDataView Members
public void ShowData()
{
_source.DataSource = _dataService.GetData();
}
#endregion
private IDataService _dataService;
private BindingSource _source = new BindingSource();
}
* This source code was highlighted with Source Code Highlighter .
But due to the fact that we resolve the data layer dependencies earlier than the UI layer plus the unusual, street magic of the Unity container, we do not have any problems with the dependency.
And this is how Tolyan implemented the IModule in his layer:
public class Module : IModule
{
#region IModule Members
public void Configure(Microsoft.Practices.Unity.IUnityContainer container)
{
container.RegisterType( typeof (IDataService), typeof (DataService));
}
#endregion
}
* This source code was highlighted with Source Code Highlighter .
3) Perhaps the main advantage of this approach is encapsulation. We operate with interfaces. We do not need to know what is behind them. With the slightest inadequacy of Tolyan or Kolyan, we simply throw away everything that he wrote, and write ours, staying (legitimately) in full confidence for the safety of other code. You just need to implement the contracts of the replaced box: D
4) Someone
sse asked a completely wonderful question in the
article about the unit:
Because for such simple examples
var svc = new MyService(10);
looks much better than
var uc = new UnityContainer();
uc.RegisterType<IService, MyService>();
uc.RegisterType(new InjectionConstructor(10));
var svc = uc.Resolve();
I think I explained his misunderstanding.
5) Generally speaking, the Unity container is intended for dependency injectors and the factory of objects of the new framework from MS - Unity Application Block. And it seems as if he tells us that it is better not to use it outside the framework walls, but the possibilities of its use are really very wide.
6) The forms and user controls specified in the code need to be refined through the MVP pattern. But this is an amateur :)
Thanks for attention:)