https://onCodeDesign.com/CraftConf2018
Enforce Consistency through
Application Infrastructure
Florin Coroș
.com
www.iQuarc.com | www.InfiniSwiss.com | onCodeDesign.com
florin.coros@iquarc.com
@florincoros
https://onCodeDesign.com/CraftConf2018
About me
@florincoros
Co-Founder & Partner
Partner
.com
Co-Founder & Partner
https://onCodeDesign.com/CraftConf2018
Why Is Consistency the Key?
3
https://onCodeDesign.com/CraftConf2018
Impeded by Others’ Code?
4
https://onCodeDesign.com/CraftConf2018
Different Solutions to the Same Problem
5
https://onCodeDesign.com/CraftConf2018
Which one is the ONE?
6
https://onCodeDesign.com/CraftConf2018
Cost of Change
Wrong approach consistently repeated
in all of the application screens
vs
Uniquely new approach in all of the
application screens
7
https://onCodeDesign.com/CraftConf2018
Managing Complexity
when projects do fail for reasons that are primarily
technical, the reason is often
uncontrolled complexity
8
https://onCodeDesign.com/CraftConf2018
Controlling the Complexity
uniqueness consistency
9
https://onCodeDesign.com/CraftConf2018
Rules Are Easy to Ignore
Quick and Dirty
vs
The only way to go fast, is to go well!
10
https://onCodeDesign.com/CraftConf2018
Quality through Discipline
11
https://onCodeDesign.com/CraftConf2018
Seniors May Be Overwhelmed with Review
12
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
Assure Consistency in Using External Library by
Hiding It
Exception Wrappers Decorators
<<Interface>>
API Interfaces<<static class>>
Log
+LogError()
+LogWarining()
14
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
Encapsulate Data Access Concerns
<<Interface>>
TDataModel
IRepository
+GetEntities<TDataModel>()
<<Interface>>
TDataModel
IUnitOfWork
+GetEntities<TDataModel>()
+SaveChanges()
Database
Repository UnitOfWork
<<Stereotype>>
<<DTO>>
Order
<<DTO>>
Person
[Service(typeof (IRepository))]
internal class EfRepository : IRepository, IDisposable
{
private readonly IDbContextFactory contextFactory;
private readonly IInterceptorsResolver interceptorsResolver;
private DbContext context;
private readonly IEnumerable<IEntityInterceptor> interceptors;
public EfRepository(IDbContextFactory contextFactory,
IInterceptorsResolver resolver)
{
this.contextFactory = contextFactory;
this.interceptorsResolver = interceptorsResolver;
this.interceptors =
resolver.GetGlobalInterceptors();
}
... iQuarcDataAccess
16
https://onCodeDesign.com/CraftConf2018
public interface IRepository
{
IQueryable<TDbEntity> GetEntities<TDbEntity>() where TDbEntity : class;
IUnitOfWork CreateUnitOfWork();
}
public interface IUnitOfWork : IRepository, IDisposable
{
void SaveChanges();
void Add<T>(T entity) where T : class;
void Delete<T>(T entity) where T : class;
void BeginTransactionScope(SimplifiedIsolationLevel isolation);
}
Create Separated Patterns for Read-Only and
Read-Write
17
https://onCodeDesign.com/CraftConf2018
Patterns for Read-Only data
public class OrdersController : Controller
{
private readonly IRepository repository;
public OrdersController(IRepository repository)
{
this.repository = repository;
}
public IActionResult Index(string customer)
{
var orders = repository.GetEntities<SalesOrderHeader>()
.Where(soh => soh.Customer.Person.LastName == customer)
.Select(soh => new OrdersListViewModel
{
CustomerName = customer,
Number = soh.SalesOrderNumber,
SalesPersonName = soh.SalesPerson,
DueDate = soh.DueDate,
});
return View(orders);
}
...
}
18
https://onCodeDesign.com/CraftConf2018
Patterns for Read-Write data
public class OrdersController : Controller
{
...
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult PlaceOrder(OrderRequestViewModel model)
{
...
using (IUnitOfWork uof = repository.CreateUnitOfWork())
{
SalesOrderHeader order = uof.GetEntities<SalesOrderHeader>()
.FirstOrDefault(o => o.CustomerID == c.ID && o.OrderDate.Month == DateTime.Now.Month);
if (order == null)
{
order = new SalesOrderHeader {Customer = c};
uof.Add(order);
}
AddRequestToOrder(model, order);
uof.SaveChanges();
}
...
}
19
https://onCodeDesign.com/CraftConf2018
Consistency Creates Optimizations Opportunities
internal class EfRepository : IRepository, IDisposable
{
public IQueryable<T> GetEntities<T>() where T : class
{
return Context.Set<T>().AsNoTracking();
}
public IUnitOfWork CreateUnitOfWork()
{
return new EfUnitOfWork(contextFactory, interceptorsResolver);
}
private sealed class EfUnitOfWork : IUnitOfWork
{
private DbContext context;
private TransactionScope transactionScope;
private readonly IDbContextFactory contextFactory;
public EfUnitOfWork(IDbContextFactory contextFactory, IInterceptorsResolver resolver)
{
this.contextFactory = contextFactory;
this.interceptorsResolver = interceptorsResolver;
}
}
20
https://onCodeDesign.com/CraftConf2018
Create Development Patters in how Data is Accessed
21
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
DIP to Move OnSave & OnLoad Logic out of Data Access
<<Interface>>
TDataModel
IEntityInterceptor
+OnLoad()
+OnSaving()
<<Interface>>
TDataModel
IDbContextFactory
+CreateContext()
DbContextFactoryUnitOfWork
Repository
24
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
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
https://onCodeDesign.com/CraftConf2018
Design PatternslikeCompositionorChain of Responsibility30
https://onCodeDesign.com/CraftConf2018
Patters in How Dependencies are Created
31
https://onCodeDesign.com/CraftConf2018
iQuarc iQuarc
Dependencies
Management
Design Patterns in
Service Composition
Enforce Consistency
through Structure
Enforce
Separation of Concerns
Patterns for
Queries & Commands
Interceptors for Queries
& Commands
Enforce Consistency through Application Infrastructure
iQuarcCode-Design-Training
onCodeDesign.comtraining
32
https://onCodeDesign.com/CraftConf2018
Florin Coroș
Co-founder & Partner, iQuarc www.iquarc.com
Co-founder & Partner, InfiniSwiss www.infiniswiss.com
.com
email: florin.coros@iquarc.com
blog: onCodeDesign.com
tweet: @florincoros

Enforce Consistency through Application Infrastructure

Editor's Notes

  • #3 My name is Florin Coros I live in Cluj-Napoca I like to go to ROCK concerts I like to travel with my girl-friend I like to play GO. GO is a game that thought me to look a few steps ahead, which turned to be very useful for me… I tweet quite a lot I am a big fan of Uncle Bob I am a Software Architect at ISDC I am one of the cofounders of RABS I am part of iQuarc
  • #4 4’ How many of you work in projects with more people? Or are in the position of reading & modifying code written by others? How many of you work alone?
  • #6 One solution to a common problem (also give DI example, using new instead of DI) Consistent way to do: Data Access Dependency Injection … Logging Error Handling Localization Authorization Drawbacks: High Cost of Change Poor SoC Time to add new features is bigger -> less efficient Error prone
  • #7 9’ He will not know which path to follow. He will not know which one is the one. Say the story ab DCV  increase complexity  increase accidental complexity When you see that in some places we use EF, in others ADO, in others stored procedures, you don’t know why and which one is the good one
  • #8 If consistency you can have one strategy, one solution with which you can fix or replace all at onece! - Its like being able to use an ReplaceALL function!
  • #9 13’
  • #10 We want our code to look like the one on the right! We want to be able to identify patterns on how our code is written I don’t necessary mean the known Design Patterns, but patterns specific to our project, Patterns that clearly say how we do things in this project, how this concern is addressed or implemented A system with patterns is going to have a manageable complexity. It is maintainable even if it is very complex and large is size Patterns make it simple A system in which everything is unique is going to be difficult to change and maintain. Even if it is small and simple, the uniqness, the chaos makes it complex
  • #11 17’ Too often “Quick and Dirty” wins over “The only way to go fast, is to go well” Do you know who says this? Yes. Uncle Bob! The initiator of the Craftsmanship movement He created this conscience that we should write clean code, because we are good developers, because we are good professionals that care ab what we are doing and about code quality This is nice but it is not enough Especially when we are under the pressure of delivery When we are encouraged to go on the Quick & Dirty death path, under the argument of being pragmatic People are going to forget these rules when they are under pressure to deliver one screen a day. Fast! Its simple to forget! Imagine the following context: 3 years estimate -> 1 year delivery So what do we do? What do we do to get a minimum level of quality, that can sustain the project over time?
  • #12 You have some people that review the code and check if rules are followed!
  • #13 Two guys who are going to review everyone’s code to check if we have the best practices followed, if we have a good SoC, if arch is followed, and if there are places where significant improvements could be done by applying Design Patterns / SOLID Principles or other principles…
  • #14 22’ no circular dependencies – achieved through You put in a structure through your design in a way that it is more difficult to write bad code than code which follows your design You put in your design the constraints. These may make your design to be more closed, less generic, but these are compromises worth doing to achieve consistency through structure
  • #15 25’ - Enforces Separation of Concern! - Libraries can be used for any kind of applications. We are not interested in building any kid of application, we are interested in building THIS specific application
  • #16 End at 28’
  • #17 Start at 28’ Encapsulates ALL data access concerns Assures a consistent way to do data access  any component that has to access data has to use a IRepository or an IUnitOfWork Constraints SoC: DA from BLL
  • #19 There is no other way to get an implementation of the IRepository than DI - IRepository used to get data for read-only - Light implementation that can only be obtained through DI - Returns IQueriable<T> - Central place for all queries to the database Testable code No need to pass IRepository as parameters!
  • #20 The UnitOfWork can only be created through this factory method on the repository The UnitOfWork is in an using statement. It has a very well defined scope It is not to be passed as parameters to methods
  • #23 34’
  • #24 We could even take out from the VS Solution the DbContextProvider project if the Context.tt generate is triggered by the SalesEntities.tt and a post build event of the DataModel assembly also builds the DbContextProvider.csproj
  • #26 43’
  • #27 Enforces Dependency Injection through Constructor only - By hiding the underlying DI Container it makes that the configuration of it can ONLY be done at app startup through annotations or conventions
  • #28 - Enforces Dependency Injection through Constructor only - By hiding the underlying DI Container it makes that the configuration of it can ONLY be done at app startup through annotations or conventions
  • #29 47’ Dependencies are visible in the constructor Dependencies are on interfaces ONLY  Loose Coupled code  testable code Dependencies are visible, so we can see classes that have too many dependencies –> poor cohesion  refactor to loose coupling The Implementation depends on INTERFACES of other services it uses and on DTOs which are paramenters
  • #31 50’
  • #33 52’
  • #35 51’
  • #36 A question I always get
  • #37 - Technical / strategic debt can be done bellow the line No Design, no trade-off: no matter how less attention I will pay to the design I will still go slow. I am impeded by the mess. Less quality -> less options -> more unexpected things -> slower you are Design: I can make the trade-off - Red team productivity NEVER decreases. See above chart with productivity going to 0. - It is important to know where you are. As long as you are under the line you may take technical debt but you must be aware! To be able to keep it under control - When not pay attention to design: - Prj finishes before the intersection point: prototype, throw away, PoC - CHALLENGE: unaware of having bad code or taking too big technical debt. It takes us by surprise - By being on the blue line with our prj most of the times we have done a great job in making our customers/managers not to trust us!
  • #38 - When does quality pay off? All projects have a slower start and then the efficiency increases. The project reaches an healthy productivity rate after the design payoff line Interesting aspects about intersection point Prj finishes before the intersection point: prototype, throw away, PoC Technical / strategic debt can be done bellow. - Cannot be done above: even if you pay less attention/effort on quality you will still need TOO much time to add a feature and the quality still drops - less quality  slower you are Above the line we were in the initial example, but without knowing it! CHALLENGE: unaware of having bad code or taking too big technical debt. It takes us by surprise I will show how by doing GOOD unit tests you can keep this under control by having indicators of when your quality drops By being on the blue line with our prj most of the times we have done a great job in making our customers/managers not to trust us! It is important to know where you are. As long as you are under the line you may take technical debt but you must be aware! To be able to keep it under control Good UT can help us knowing when our quality drops and we sleep over the line
  • #39 All projects have a slower start and then the efficiency increases. The project reaches an healthy productivity rate after the design payoff line