YOW West 2017 OUR SPONSORS
Cost of a dependency
Lee Campbell Author of eBook and website IntroToRx.com Dev at Cashies Conference Circuit Trainer – Practical Rx
Project success
Longevity How long will your project need to be used for? How long will it need to be maintained for? How long will each of it’s dependent parts be relevant for?
Freedoms Single man dependencies vs Fungible assets Technology lock-in vs Technology agnostic
A case study A case study A COLLECTION OF EXPERIENCES
How did it get like this? COMPOUNDING EFFECTS
1. Single repo
Single repo \GUI \Server \Datastore
Single repo \GUI ◦ FeatureA ◦ FeatureB \Server ◦ FeatureA ◦ FeatureB ◦ Shared \Datastore
Single repo \GUI \Datastore ◦ FeatureA ◦ FeatureB ◦ FeatureC \Server ◦ FeatureA ◦ FeatureB ◦ FeatureC ◦ Shared ◦ Logic ◦ Scripts (Builds, Deployments)
Single repo \GUI \Datastore ◦ FeatureA ◦ FeatureA ◦ FeatureB ◦ FeatureB ◦ FeatureC ◦ FeatureC \Server ◦ FeatureA ◦ FeatureB ◦ FeatureC ◦ Shared ◦ Logic ◦ Scripts (Builds, Deployments)
Single repo \GUI \Framework ◦ FeatureA ◦ Comms Layer ◦ FeatureB ◦ Data Persistence abstraction ◦ FeatureC ◦ Scripts (Builds, Deployments) ◦ Shared \Server \Datastore ◦ FeatureA ◦ FeatureA ◦ FeatureB ◦ FeatureB ◦ FeatureC ◦ FeatureC ◦ Shared ◦ Logic ◦ Contracts
Single repo Who defines requirements and budget for each part of the system? Can this progression be maintained for the expected lifetime of the system? Do these things change together?
Single repo - Consequences Build times increase Testing burden Friction to innovate Blurry boundaries Lack of responsibility
2. Depend on abstractions
Interface all the things View Models Translators Validators
Consequences Testing now becomes an exercise in mocking Debugging is always a case of hitting finding "implementations of"
Consequences Focus is diluted Cognitive load is increased through indirection
Depend on (external) abstractions We can also take dependencies on interface define by someone else So maybe you just mock out your ◦ IoC Continer ◦ SqlConnection ◦ ORM UnitOfWork But these are normally defined in their package, with the implementation.
3. Deep layering
Deep layering Deep system dependencies
Deep layering
Deep layering My application ◦ My libraries ◦ My Framework ◦ My Framework subsystem ◦ Generic public packages ◦ Low level generic public packages
Deep layering My application ◦ My libraries (Domain, Data Access, Comms, Controls, Styles) ◦ My Framework (DataAccess, Comms, Contracts, Generic Utilities, Controls, Styles) ◦ My Framework subsystem (Implementation specific DataAccess, Comms etc) ◦ Generic public packages (ORM, Serializers, Web Platform, Messaging, Document manipulation) ◦ Low level generic public packages (Network interfaces, Collections Libraries)
Deep layering Non linear growth 3 deep with each dependency having 3 children 1 + 3 + 9 = 13 total
Deep layering Non linear growth Add 2 more layers (5 layers) 1 1 1 1 13 13 13 13 13 13 13 13 13 1 + 3 + (9 * 13) = 121 total
Deep layering Non linear growth Add 2 more layers (7 layers) 1 1 1 1 121 121 121 121 121 121 121 121 121 1 + 3 + (9 * 121) = 1093 total
+ baggage Packages that include ◦ minified and original resource ◦ Documentation (JavaDoc/Intellisense) ◦ Tooling
Deep call stacks I depend on abstractions But they also depend on abstractions So I have deep call stacks
Deep call stacks https://twitter.com/gregyoung/status/ 734713437146734592
Consequences Cognitive load increases exponentially with each layer Technical freedom is slowly eroded as each layer add more dependencies
Compounding Consequences
Compounding consequences Build times Test times Lack of Ownership
Compensating behaviour Magic Build scripts Using the real IoC Container in tests AOP
Complicated becomes complex Cant be rationalised about Emergent behaviour (bugs, not features)
Alternatives IS THERE A BETTER WAY?
Single repo FEATURE DEPENDENCY
If it changes together, it lives together Ways to identify ◦ The person that has final say on requirements is different ◦ Release cadences are different ◦ Code change cadence is different ◦ Failures are ignored "Decompose your applications based on volatility" - Juval Lowy
Single repo \GUI \Framework ◦ FeatureA ◦ Comms Layer ◦ FeatureB ◦ Data Persistence abstraction ◦ FeatureC ◦ Scripts (Builds, Deployments) ◦ Shared \Server \Datastore ◦ FeatureA ◦ FeatureA ◦ FeatureB ◦ FeatureB ◦ FeatureC ◦ FeatureC ◦ Shared ◦ Logic ◦ Contracts
Multi repo FeatureA ◦ GUI ◦ GUI ◦ Server ◦ DataStore ◦ Server ◦ DataStore ◦ Contracts ◦ Contracts GUI Lib FeatureB Comms Lib ◦ GUI Persistence Lib ◦ Server ◦ DataStore ◦ Contracts FeatureC
Multi repo Inherently parallel to build and test Forces the discussion about design by contract Can reduce the knee jerk reaction to share code
Encapsulation
Justify the interface What is the benefit of this being injected? Can it be encapsulated into this unit?
Private by default Instead of Reuse being a goal, aim for encapsulation. Rule of 3 Reuse is an evolution
The snip rule Single line to cut Avoid dependencies that require finesse to remove
Code in action A DOMAIN MODEL WITH NO DEPENDENCIES
Only depends on .NET
So how does it do anything The Domain Model defines the interfaces for the things it needs ◦ IRepository<T> Can you tell what technology we are using here? Consumers are responsible to implement it We are isolating the business logic from our technology choices.
Inversion of Control
Hosts compose shallow dependencies Hexagonal Architecture (Alistair Cockburn) Ports and Adapters Onion Architecture
We define our Ports IRepository is one of our ports We may have a SQLServer adapter, or perhaps a MongoDB one
Composition App Service Domain Model Data Service Comms Service Messaging ORM Data store Serialization platform
Composition App Service Data Comms adapter adapter Data Domain Comms store Model
Adapters CODE BREAK OUT
Composition instead of AOP I can just use normal old Decorator pattern
Results Multi repo ◦ Division of labour ◦ Cohesive code base ◦ Reduced cognitive load ◦ Technical freedom
Results Encapsulation ◦ Reduced cognitive load ◦ Less busy work ◦ Snip rule
Results Inversion of Dependencies / Ports and Adapter ◦ Focused code ◦ Fast tests ◦ Shallow stacks ◦ Technical Freedom
Know your costs Cognitive load Onboarding friction Technical Freedom Time to prod
More information LeeCampbell.com Implementing Domain Driven Design – Vaugh Vernon Greg Young – Good enough software Hex Arch – Alistair Cockburn Having the computer to do what you want isn’t the hard part, having a person understand the intent of your code is.
Recommend
More recommend