22
Ed Blackburn Freelance Developer @ejblackburn http://ejb.name

Ed Blackburn NDC

Embed Size (px)

Citation preview

Ed BlackburnFreelance Developer

@ejblackburnhttp://ejb.name

Oyster CardJuly 2003

© “Oyster Card" by Frank Murmann is licensed under CC BY3.0

Contactless2014

“Prove it”

Components• Identity Management

• Customer Services• Customer Portal

• Payment and Risk Services

• Canonical, versioned reference data

• Path finding fare generation for routes

• Journey Construction, apportionment, capping and fares

“Behaviour”© “Relativity" by M. C. Escheris, 1953

Evolving the MVP

• Revenue Protection

• Disruptions

• Travel Products (Weekly, Monthly, Annual…)

Philosophy• Object-Orientation; an object has both state

(data) and behaviour (logic)

• Domain model focused

• Tell don’t ask

• SOLID

“Tell Don’t Ask”https://pragprog.com/articles/tell-dont-ask

Tell Don’t Askpublic class Journey{

…public void AssociateRevenueInspectionTap(Tap tap) {..};public void CounterZeroRateDueToRevenueProtection()

{..};…

}

Tell Don’t Ask?public class Journey : Entity<Journey, JourneyKey>, IComparable<Journey>, IComparable<Tap>, ITapSequence, ICanProtectRevenue, IJourneyAffectedByRevenueInspection, IMayNotExist, IHaveTravelDayKey, IRateable, IDisruptable{

…public void AssociateRevenueInspectionTap(Tap tap) {..};public void CounterZeroRateDueToRevenueProtection() {..};public bool IsCounteredBy(Tap candidate) {..};public bool Exists { get; }public bool ContainsRevenueInspectionTap { get; }…

}

internal IRule<Tap> CreateRule(TravelDay travelDay){

// Create rulesreturn Rule.Create(..);

}

private IRule<Tap> CreateRule(TravelDay travelDay){

// Create rulesreturn Rule.Create(specification: new

IsRITWithinPreviousJourney(travelDay),ifSatisfied: ScenarioT01,ifNotSatisfied: Rule.Create(..));

}

private IRule<Tap> CreateRule(TravelDay travelDay){

// Create rulesreturn Rule.Create(specification: new

IsRITWithinPreviousJourney(travelDay),ifSatisfied: ScenarioT01,ifNotSatisfied: Rule.Create(

specification: new IsPreviousJourneyDisrupted(travelDay), ifSatisfied: ScenarioTDIS01, ifNotSatisfied: ScenarioT02 ));}

public sealed class IsTapTypeRailInspection : Specification<Tap>{

private static readonly ISpecification<Tap> _compositeSpecification = new IsTapModeRail().And(new IsTapTypeRevenueInspection());

protected override bool IsSatisfiedBy(Tap candidate) { return _compositeSpecification.IsSatisfiedBy(candidate);

}}

“Small Is Beautiful”• E. F. Schumacher

• To counter Bigger is Better

• Empowers developers to build complex features through small lego pieces

• Easy to read

Aggregate

“Nothing outside the Aggregate boundary can hold a reference to anything inside,

except to the root entity…”

- Eric Evans, Domain Driven Design, 2004

Aggregates & Continuations?

public class Card : Entity<Card, TravelTokenId> {

public void PerformCommand(IRuleCommand<Journey> command) { PerformCommand(command, () => _journeys.Select(item => … ) }

private void PerformCommand<TEntity>(IRuleCommand<TEntity> command,

Func<IEnumerable<TEntity>> getEntities) { Rule.Create(command).Process(getEntities()); }

…}

Pipelines and decorators

public interface ICardOperation{

Card Process(Card card);}

public interface IDecoratedCardOperation{

ICardOperation InnerCardOperation { get; private set; }}

public class RevenueProtection : ICardOperation, IDecoratedCardOperation{

public RevenueProtection (...){

...}

public Card Process (Card card){

card = InnerCardOperation.Process (card);...return card;

}

public ICardOperation InnerCardOperation { get;set;}}

Summary• Abstractions rot code

• Small interfaces

• If you have a DTO reconsider your approach

• Even anaemic models can be encapsulated

• Immutability is your friend

Future?• Token based software system

• Anything can create a token

• Vehicle registration plate

• Phone / wearable

• APIs (dog fooding with mobile)

• Data, data, data…