Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Architecture and Design Validation in .Net

Architecture

As a noun architecture can be summarised as being about structure and is about the decomposition of a product into a collection of components and interactions.
Software Architecture for Developers by @simonbrown

Validation

The assurance that a product, service, or system meets the needs of the customer and other identified stakeholders

Parkinson's law of triviality

Organizations give disproportionate weight to trivial issues
by Cyril Northcote Parkinson

FreeBSD not bikeshad

more at bikeshed.com

Why?

Type Name Collisions

Type Name Collisions with ReSharper

What to choose: Iesi.Collections.ISet, System.Collections.Generic.ISet or NPOI.Util.Collections.ISet?

Fail fast!

A fail-fast system is designed to immediately report any failure or condition that is likely to lead to failure

Test Drive the System Architecture

Test Driven Development

First we'll solve the "that works" part of the problem, then we'll solve the "clean code" part

Architecture Driven Development

Solve "clean code" first, then scramble around trying to integrate into the design the things you learn as you solve the "that works" problem

TDD by examples by @kentbeck

Reflection vs. Introspection

C# code and syntax tree

From Roslyn CTP

public object ReturnNullReferenceType()
{
    return null;
}
Roslyn syntax tree sample

Search for return null;

Func<StatementSyntax, bool> returnNullStatement =
        PredicateBuilder
            .True<StatementSyntax>()
            // all return statements
            .And(s => s is ReturnStatementSyntax)
            // with expression
            .And(s => (s as ReturnStatementSyntax).Expression != null)
            // with expression "null"
            .And(s => (s as ReturnStatementSyntax).Expression.Kind == SyntaxKind.NullLiteralExpression)
                .Compile();

Search for return default(T);

Where T is reference type

ExpressionSyntax expressionSyntax = (statement as ReturnStatementSyntax).Expression;

ISemanticModel semanticModel = await semanticModelAsync;

return expressionSyntax != null &&
     (semanticModel.GetTypeInfo(expressionSyntax).Type == null
         || semanticModel.GetTypeInfo(expressionSyntax).Type.IsReferenceType);

Advanced samples

Limit references to assemblies

Using System.Reflection

[Test, Combinatorial]
public void TestReferencesIntersection(
    [ValueSource("Assemblies")] Assembly leftAssembly,
    [ValueSource("Assemblies")] Assembly rightAssembly)
{
    if (leftAssembly == rightAssembly) return;

    var rightReferences = FilterOutAssemblies(rightAssembly);
    var leftReferences = FilterOutAssemblies(leftAssembly);

    Assert.That(leftReferences.Intersect(rightReferences), 
                Is.Not.Null.And.Empty);
}

private static IEnumerable<string> FilterOutAssemblies(Assembly source)
{
    return new HashSet<string>(source
        .GetReferencedAssemblies()
        .Where(assembly => assembly.Name != @"mscorlib"
                            && assembly.Name != @"Microsoft.CSharp"
                            && !assembly.Name.StartsWith(@"System")
                            && !assembly.Name.StartsWith(@"AutoMapper")
                            && !assembly.Name.StartsWith(@"nCrunch.TestRuntime")
                            && !assembly.Name.StartsWith(@"PostSharp"))
        .Select(assembly => assembly.Name));
}

Limitations

Assertions

ApprovalTests

Capturing Human Intelligence

Introspection assertions using ApprovalTests

Performance/complexity testing

[Test, Timeout(2000)] // <-- brittle unit tests!!!
[Description("Find collinear points among 1000 random distinct points")]
public void Timing_FindCollinearPoints1kRandomDistinctPoints_ExpectToFinishIn2Seconds() 
{
    /* Actual test */
}

Algorithms timing/complexity testing

Algorithms complexity testing

[Test, Timeout(2000)]
[Description("Compare complexity of two solutions of sizes 64 and 256 to be less then 5.4")]
public void Timing_CompareComplexityOfTwoSolution64and256_ExpectToInRange() 
{
    var partialMockedProblem64 = mockRepository.PartialMock<AlgorithmImpl>(/* initialize problem size */);
    var partialMockedProblem256 = mockRepository.PartialMock<AlgorithmImpl>(/* initialize problem size */);
    /* and so on… */
    
    partialMockedProblem64
        .Expect(m => m.DoCalculations(/* arguments */))
        .CallOriginalMethod(OriginalCallOptions.CreateExpectation)
        .WhenCalled(invok => nTimesDoCalculationsCalledProblem64++)
        .Repeat.AtLeastOnce();
    /* and so on… */
    
    mockRepository.ReplayAll();
    
    /* Act */
    
    /* Assert */
    mockRepository.VerifyAll();
    var cmp64 = nTimesDoCalculationsCalledProblem64 * Math.Log(nTimesDoCalculationsCalledProblem64);
    var cmp256 = nTimesDoCalculationsCalledProblem256 * Math.Log(nTimesDoCalculationsCalledProblem256);
    Assert.That(cmp256 / cmp64 < 5.4); /* ask BA with PhD why? :) */
    /* and so on… */
}
 

PostSharp: Architecture Constraints

.Net build pipeline

C# source code
Compilation:
CIL (Common Intermediate Language)
Run-time:
CLR(Common Language Runtime) + Machine Instruction

PostSharp build pipeline

Rewrite:
Modified CLI (Common Intermediate Language)
CompileTimeValidate

Check constraints

CompileTimeInitialize

Serialize state for runtime

Architecture Framework

POCO class

class with typical code issues

public class Customer
{
    // This property is defined as expected
    public virtual int Id { get; set; }

    // without `virtual` keyword PostSharp will generate compile-time error message
    // otherwise NHiberante will generate exception during run-time
    public /*virtual*/ string Name { get; set; } 

    // Both getter/setter are required to be public
    internal virtual string Description { get; private set; }
}

Validate constraint

Validate that all properties are public, virtual with getter and setter

[Serializable]
[MulticastAttributeUsage(MulticastTargets.Property)]
public class VirtualKeywordRequiredForInstancePropertiesAttribute : ScalarConstraint
{
    // Validation happens only at post-compile phase
    public override void ValidateCode(object target)
    {
        var propertyInfo = (PropertyInfo)target;
        var targetType = propertyInfo.DeclaringType;

        if (targetType != null)
        {
            // check that property is public, virtual with getter and setter
            var virtualInstanceProperty = targetType
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(propInfo => propInfo.CanRead & propertyInfo.CanWrite)
                .Where(propInfo => propInfo.GetGetMethod().IsVirtual)
                .Where(propInfo => propInfo.GetSetMethod().IsVirtual)
                .SingleOrDefault(propInfo => propInfo == propertyInfo);

            // generate compile time error
            if (virtualInstanceProperty == null)
            {
                Message.Write(MessageLocation.Of(targetType),
                                SeverityType.Error,
                                "998",
                                "Property '{0}' in Entity class {1} show be public, virtual with both getter and setter",
                                propertyInfo.Name, targetType.FullName);
            }
        }
    }
}

Multicasting

// Multicast aspect to all properties of classes in namespace
[assembly: VirtualKeywordRequiredForInstanceProperties(
            AttributeTargetTypes = "CodeSmells.FakeDataAccessLibrary.Entity.*", 
            AttributeTargetElements = MulticastTargets.Property)]

Compile-time error

Error generated by PostSharp aspect validation

Compile-time error generated by PostSharp aspect validation

Advanced samples

Limitations

If you perceive that there are four possible ways in which something can go wrong, and circumvent these, then a fifth way, unprepared for, will promptly develop
by Edward Aloysius Murphy

References

Use a spacebar or arrow keys to navigate