Upload
itcamp
View
87
Download
1
Tags:
Embed Size (px)
DESCRIPTION
Badly designed code is one of the highest price we pay as an industry. In many cases the cost of adding or changing features increases with the time we advance into the project. The bigger the project gets, the more complex it gets and the more costly it is to work on it, if it lacks structure. In this session I will show from real world projects experience how to keep your team efficient and how to reduce the cost of change with what I call the Application Software Infrastructure. It gives the means to control the size and complexity in large projects. It assures code quality through structure rather than relying on discipline only. It documents the architecture into code and bridges this way the architectural diagrams and the code structures.
Citation preview
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Code Quality through Application Software Infrastructure
Florin Coros
@florincoros
blog.iquarc.com/florin
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Huge thanks to our sponsors & partners!
Premium community conference on Microsoft technologies itcampro@ itcamp14#
About me
Co-Founder
Software Architect@florincoros
Founder & Partner
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Inspired
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Why Code Quality?
Premium community conference on Microsoft technologies itcampro@ itcamp14#
How to Get to Code Quality?
Architecture
App Software Infrastructure
Vertical Slice
ReviewGood UTRefactor
DeliverFunctionality
DeliverFunctionality
DeliverFunctionality
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Application Infrastructure
Premium community conference on Microsoft technologies itcampro@ itcamp14#
The Application Foundation
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Wrongly Built House
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Different Solutions to the Same Problem
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Which one is the ONE?
Premium community conference on Microsoft technologies itcampro@ itcamp14#
when projects do fail for reasons that are primarily technical, the reason is
often
uncontrolled complexity
Importance of Managing Complexity
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Importance of Managing Complexity
Scalability
Availability
Multitenancy
Security
Authorization
Premium community conference on Microsoft technologies itcampro@ itcamp14#
App Infrastructure Enforces the Architecture
Premium community conference on Microsoft technologies itcampro@ itcamp14#
App Infrastructure – Primary TOOL to Control Complexity and Size
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Implementing a Complex and Large System
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Good Design vs Bad Design
http://martinfowler.com/bliki/DesignStaminaHypothesis.html
Premium community conference on Microsoft technologies itcampro@ itcamp14#
<<Interface>>
IModule
+ Initialize()
Application
+ Initialize()*
+ Modules[]
AppModule1
+ Initialize()
AppModule2
+ Initialize()
• Modular Application Support
• Application modules do not depend on Frameworks
• Application can be hosted in any .NET process
App Boot: Modularity
3…..
\iQuarc
Premium community conference on Microsoft technologies itcampro@ itcamp14#
• Makes Programming Against Interfaces the de facto programming model
• Abstracts and hides the Dependency Container
• Provides a maintainable mechanism for declaring services implementation
• Dictates how Dependency Injection is used
App Boot: Dependency Injection
<<Attribute>>
ServiceAttribute
+ ServiceAttribute()+ServiceAttribute(Type contract)+ ServiceAttribute(Type t, Lifetime lifetime)
Bootstrapper
+Bootstrapper(Assembly[] assemblies)+Run()
\iQuarc
Premium community conference on Microsoft technologies itcampro@ itcamp14#
[WcfService(typeof(IPolicyAdministrationService))]
public class PolicyAdministrationService : IPolicyAdministrationService
{
private readonly IPolicyClassificationService classificationService;
private readonly IRepository repository;
public PolicyAdministrationService(IPolicyClassificationService
. classificationService, IRepository repository)
{
this.classificationService = classificationService;
this.repository = repository;
}
// . . .
}
[Service(typeof(IPolicyClassificationService))]class PolicyClassificationService : IPolicyClassificationService
{
private readonly IRepository repository;
public PolicyClassificationService(IRepository repository)
{
this.repository = repository;
}
//. . .
}
[Service(typeof(IRepository))]class Repository : IRepository
{
//...
}
App Boot: Declaring Service Implementations
• Allows only Constructor Dependency Injection
• Container configuration is not of the concerns of the application code
• Favors good code design• developers think in terms of services
• Opens opportunities to extend it with other discovery mechanisms• WCF Service
Premium community conference on Microsoft technologies itcampro@ itcamp14#
public interface IOrderApproval{
bool ApproveOrder(Order o);}
[Service(typeof(IOrderApproval))]class CompositeOrderAprovalService : IOrderApproval{private readonly IEnumerable<IOrderApproval> approveActions;
public CompositeOrderAprovalService(IOrderApproval[] approvals){
this.approveActions = approvals.OrderByPriority();}
public bool ApproveOrder(Order o){
return approveActions.All(action => action.ApproveOrder(o));}
}
[Priority(Priorities.High)][Service("Stock Approval", typeof(IOrderApproval))]class StockOrderApproval : IOrderApproval{
private readonly IRepository repository;
public StockOrderApproval(IRepository repository){
this.repository = repository;}
public bool ApproveOrder(Order o){
//...}
}
[Priority(Priorities.Medium)]
[Service("Customer Approval", typeof(IOrderApproval))]
class CustomerOrderApproval : IOrderApproval
{
private readonly IRepository repository;
public CustomerOrderApproval(IRepository repository)
{
this.repository = repository;
}
public bool ApproveOrder(Order o)
{
//...
}
}
[Priority(Priorities.Medium)]
[Service("Ammount Approval", typeof(IOrderApproval))]
class OrderAmountApproval : IOrderApproval
{
private readonly IOrderCalculationsService calculationSrv;
public OrderAmountApproval(IOrderCalculationsService srv)
{
this.calculationsService = calculationsService;
}
public bool ApproveOrder(Order o)
{
//...
}
}
App Boot: Favors Composition and OCS Principle
Premium community conference on Microsoft technologies itcampro@ itcamp14#
public IEnumerable<Policy> GetPolicyFromMutation(Mutation
mutation)
{
var policies = repository.GetEntities<PolicyImage>()
.Where(image => image.MutationId==mutation.Id)
.Select(image => new Policy
{
Id = image.PolicyId,
ValidAt = image.Mutation.Date
//..
}).ToList();
// do other calculations / grouping on data
return policies;
}
public void CalculateChanges(ChangeEvent change)
{
using (var uof = repository.CreateUnitOfWork())
{
var policies = uof.GetEntities<PolicyImage>()
.Where(i => i.Status == ImageStatus.Calculating &&
. i.EvendId == change.Id);
foreach (PolicyImage image in policies)
{
image.Ammunt = GetAmmountFor(image, change);
}
uof.SaveChanges();
}
}
Encapsulate Data Access Concerns
<<Interface>>
IRepository
+GetEntities<T>() : IQueriable<T>
<<Interface>>
IUnitOfWork
+SaveChanges()
Database
Repository UnitOfWork
<<Stereotype>><<DTO>>
Plan<<DTO>>
Contract
+CreateUnitOfWork() +GetEntities<T>():IQueriable<T>
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Consistency through StructureFunctional Modules
Module1
DataModel
Module2.Services
Contracts
Infrastructure
Technical Modules
CRUD.Services
CRUD.Web
Core Infrastructure
Data Access
Web Application
ServicesInfra
ApplicationBoot
WFC Application
WebInfra
CommonInfra
Module1.Web
Module2
DataModel
Module2.Web
Module2.Services
Premium community conference on Microsoft technologies itcampro@ itcamp14#
RepositoryImpl
+ GetEntities<T>() : IQueriable()
IRepository IUnitOfWork
+ SaveChanges()
Consistent Data Access -> Low Extension Costs
Logging Query Execution
Premium community conference on Microsoft technologies itcampro@ itcamp14#
RepositoryImpl
+ GetEntities<T>() : IQueriable()
IRepository IUnitOfWork
+ SaveChanges()
Consistent Data Access -> Low Extension Costs
Data Consistency Validation
public interface IEntityInterceptor<T> : IEntityInterceptorwhere T : class
{void OnLoad(IEntityEntryFacade<T> entry, IRepository rep);void OnSave(IEntityEntryFacade<T> entry, IRepository rep);void OnEntityRemoved(IEntityEntryFacade<T> entity,
. IRepository rep);}
public class Repository : IRepository{//...void InterceptSave(DbContext context, List<object> intercept){
var modifiedEntities = GetModifiedEntities(context);
foreach (object entity in modifiedEntities){
Intercept(allInterceptors, entity,(i, e) => i.OnSave(e, this));
}}
}
Premium community conference on Microsoft technologies itcampro@ itcamp14#
RepositoryImpl
+ GetEntities<T>() : IQueriable()
IRepository IUnitOfWork
+ SaveChanges()
Consistent Data Access -> Low Extension Costs
Auditing
public interface IAuditable{
DateTime? LastEditDate { get; set; }DateTime CreationDate { get; set; }string LastEditBy { get; set; }string CreatedBy { get; set; }
}
[Service("AuditableInterceptor", typeof(IEntityInterceptor))]public class AuditableInterceptor : Interceptor<IAuditable>{public override void OnSave(IEntityEntryFacade<IAuditable> . entity, IRepository rep){
// ...if (entity.State == EntityEntryStates.Added){
entity.Entity.CreationDate = systemDate;entity.Entity.CreatedBy = userName;
}
entity.Entity.LastEditDate = systemDate;entity.Entity.LastEditBy = userName;
}}
Premium community conference on Microsoft technologies itcampro@ itcamp14#
RepositoryImpl
+ GetEntities<T>() : IQueriable()
IRepository IUnitOfWork
+ SaveChanges()
Consistent Data Access -> Low Extension Costs
Data Authorization
public IQueryable<T> GetEntities<T>(){
IQueryable<T> entities = GetEntitiesInternal<T>();
IQueryable<T> filtered = FilterByBrand(entities);
return filtered;}
private IQueryable<T> FilterByBrand<T>(IQueryable<T> entities){int brandLabelId = ClaimsPrincipal.Current
.GetClaimValue<int>(Claims.BrandLabelId);
Expression<Func<T, int?>> selector=SelectBrandIdFrom(entities);
var condition =ExpressionBuilder.BuildWhereExpression(selector, brandLabelId, ExpressionType.Equal);
return entities.Where(condition);}
Premium community conference on Microsoft technologies itcampro@ itcamp14#
RepositoryImpl
+ GetEntities<T>() : IQueriable()
IRepository IUnitOfWork
+ SaveChanges()
Consistent Data Access -> Low Extension CostsData Localization
private IQueryable<T> GetEntitiesInternal<T>(){
DbSet<T> set = context.Set<T>();
return new DataLocalizationQueryable<T>(queryableSet); }
class DataLocalizationQueryable<T> : IOrderedQueryable<T>{private DataLocalizationExpressionVisitor translationVisitor;private IQueryable<T> internalQuery;
public DataLocalizationQueryable(IQueryable<T> query){internalQuery = query;translationVisitor = new DataLocalizationExpressionVisitor();Provider = new DataLocalizationQueryProvider(query.Provider,
this.translationVisitor);
this.ElementType = typeof(T);}
public IEnumerator<T> GetEnumerator(){
return internalQuery.Provider.CreateQuery<T>(translationVisitor.Visit(internalQuery.Expression)).GetEnumerator();
}
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Decomposition by Volatility
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Generic CRUD Screens / Services
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Edit Contract
Monthly
Never
0
Recalculation Period
Recalculation
OKCancel
Minimum Cost: No
Minimum Amount For Cost Recalculation:
Yes
// Contract cost fields are UNAVAILABLE when ...
container
.Register(c => c.RecalculationPeriod_ID,
c => c.CostType.Recalculation != Never);
.Register(c => c.MinAmount,
c => c.CostType.MinAmountForCost == false);
Generic Implementation for Fields Availability
Edit Contract
Monthly
Periodical
0
Recalculation Period
Recalculation
OKCancel
Minimum Cost: No
Minimum Amount For Cost Recalculation:
Yes
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Application Infrastructure – Shaping to Context
.NET Framework
WF
WCF
WEB API
ASP.NET
Entity Framework
. . . Unity
NServiceBus Log4Net
. . .
Application Infrastructure
Application Functionalities
Premium community conference on Microsoft technologies itcampro@ itcamp14#
How to do it?cu
mu
lati
ve f
un
ctio
nal
ity
time
completion
constructionready
design payoff
Premium community conference on Microsoft technologies itcampro@ itcamp14#
How to do it?cu
mu
lati
ve f
un
ctio
nal
ity
time
completion
constructionready
design payoff
time
Arc
Arc
Infr
a
Fun
ctio
nal
ity
Infr
a Fun
ctio
nal
ity
Infr
a Fun
ctio
nal
ity
Fun
ctio
nal
ity
Fun
ctio
nal
ity
Fun
ctio
nal
ity
RepositoryImpl
+ GetEntities<T>() : IQueriable()
IRepository IUnitOfWork
+ SaveChanges()
IModule
<<Attribute>>
ServiceAttribute
\iQuarc
Fun
ctio
nal
ity
Premium community conference on Microsoft technologies itcampro@ itcamp14#
Thank You!