When projects do fail for reasons that are primary technical, the reason is often uncontrolled complexity. The complexity goes out of hand when the code lacks structure. In large software projects where many developers work on the same code base one of the biggest challenge is to get consistency in code, to create development patterns for common problems, so you can control the complexity and size of the system.
In this session I will show how we can achieve consistency through structure, rather then relying on discipline only. We will look at some basic building blocks of an application infrastructure which will enforce the way dependencies are created, how dependency injection is used or how separation of the data access concerns is enforced.
13. https://onCodeDesign.com/DevTalks2018
Quality through Structure
You enforce consistency with structure
Create a structure that makes difficult to write bad
code rather then code that follows the design
You use assemblies and references among them to
enforce rules
Hide external frameworks to enforce the way they are
used
Enforce Constructor Dependency Injection and
encourage Programming Against Interfaces
13
15. https://onCodeDesign.com/DevTalks2018
public interface ILog
{
void Info(string message, params object[] args);
void Warn(string message, params object[] args);
void Error(string message, Exception ex);
}
Enforces Separation of Concerns
The application code only knows about ILog
Once I is defined we can develop screens and call
this interface to log traces or errors
The real implementation can be done later
If logging needs changes, we can modify the
interfaces at once in all places it is used
The implementation is in only one place, so one place to
change only
15
22. https://onCodeDesign.com/DevTalks2018
Enforce Separation of Data Access Concerns
<<Interface>>
TDataModel
IRepository
+GetEntities<TDataModel>()
<<Interface>>
TDataModel
IUnitOfWork
+GetEntities<TDataModel>()
+SaveChanges()
Database
Repository UnitOfWork
<<Stereotype>>
<<DTO>>
Order<<DTO>>
Person
22
23. https://onCodeDesign.com/DevTalks2018
DIP to Enforce Separation of Data Access Concern
<<Interface>>
TDataModel
IEntityInterceptor
+OnLoad()
+OnSaving()
<<Interface>>
TDataModel
IDbContextFactory
+CreateContext()
Database
<<DTO>>
Customer<<DTO>>
Order <<DTO>>
Person
DbContextFactoryUnitOfWork
Repository
23
24. https://onCodeDesign.com/DevTalks2018
DIP to Move OnSave & OnLoad Logic out of Data Access
<<Interface>>
TDataModel
IEntityInterceptor
+OnLoad()
+OnSaving()
<<Interface>>
TDataModel
IDbContextFactory
+CreateContext()
DbContextFactoryUnitOfWork
Repository
24
25. https://onCodeDesign.com/DevTalks2018
AppBoot: DI Abstractions & Type Descovery
<<Attribute>>
ServiceAttribute
IModule
<<Interface>>
TDataModel
IEntityInterceptor
+OnLoad()
+OnSaving()
<<Interface>>
TDataModel
IRepository
Database
<<DTO>>
Customer<<DTO>>
Order<<DTO>>
Person
DbContextFactoryUnitOfWork
Repository
<<Interface>>
TDataModel
IUnitOfWork
25
26. https://onCodeDesign.com/DevTalks2018
AppBoot Enforces Constructor Dependency Injection
<<Attribute>>
ServiceAttribute
+ ServiceAttribute()
+ServiceAttribute(Type contract)
+ ServiceAttribute(Type t, Lifetime lifetime)
Bootstrapper
+Bootstrapper(Assembly[] assemblies)
+Run()
public interface IPriceCalculator
{
int CalculateTaxes(Order o, Customer c);
int CalculateDiscount(Order o, Customer c);
}
[Service(typeof(IPriceCalculator), Lifetime.Instance)]
interal class PriceCalculator : IPriceCalculator
{
public int CalculateTaxes(Order o, Customer c)
{
return 10; // do actual calculation
}
public int CalculateDiscount(Order o, Customer c)
{
return 20; // do actual calculation
}
}
iQuarcAppBoot
26
27. https://onCodeDesign.com/DevTalks2018
Software systems should separate the startup process, when the application objects are constructed and the
dependencies are “wired” together, from the runtime logic that takes over after startup
<<Interface>>
IModule
+ Initialize()
Application
+ Initialize()
*
+ Modules[]
AppModule1
+ Initialize()
AppModule2
+ Initialize()
Enforce Separation of Construction from Use
public static void Main(string[] args)
{
var assemblies = GetApplicationAssemblies().ToArray();
Bootstrapper bootstrapper = new Bootstrapper(assemblies);
bootstrapper.ConfigureWithUnity();
bootstrapper.AddRegistrationBehavior(new ServiceRegistrationBehavior());
bootstrapper.Run();
}
27
28. https://onCodeDesign.com/DevTalks2018
Patters forCreating Services thatDepend Only onInterfaces
[Service(typeof (IOrderingService))]
public class OrderingService : IOrderingService
{
private readonly IRepository repository;
private readonly IPriceCalculator calculator;
private readonly IApprovalService orderApproval;
public OrderingService(IRepository repository, IPriceCalculator calculator, IApprovalService orderApproval)
{
this.repository = repository;
this.calculator = calculator;
this.orderApproval = orderApproval;
}
public SalesOrderInfo[] GetOrdersInfo(string customerName)
{
var orders = repository.GetEntities<SalesOrderHeader>()
...
return orders.ToArray();
}
public SalesOrderResult PlaceOrder(string customerName, OrderRequest request)
{
...
}
}
28
29. https://onCodeDesign.com/DevTalks2018
Enforce Constructor DI to Prevent Circular Dependencies
[Service(typeof(IApprovalService))]
class ApprovalService : IApprovalService
{
private readonly IPriceCalculator priceCalculator;
public ApprovalService(IPriceCalculator priceCalculator)
{
this.priceCalculator = priceCalculator;
}
...
}
[Service(typeof (IPriceCalculator), Lifetime.Instance)]
public class PriceCalculator : IPriceCalculator
{
private readonly IApprovalService approvalService;
public PriceCalculator(IApprovalService approvalService)
{
this.approvalService = approvalService;
}
...
}
29