Hendry Luk — Sheep in Fence

March 9, 2012

SheepAspect: Mixin

SheepAspect Preview-3 was released today, and one of the most important new features is Mixins. In Preview-2, there were only one advice implementation: [Around]. In preview-3 we introduce two more advices: [DeclareMixin] and [DeclareAttributes].

[DeclareMixin] advice allows you to add new interface implementations on existing classes. As an example, here is a plain simple Customer class:

public class Customer
{
	public string FirstName {get; set;}
	public string LastName {get; set;}
}

And a completely unrelated IEmailable interface:

public IEmailable
{
	public string EmailAddress {get; set;}
	public void SendEmail(string subject, string body);
}

Now let’s stitch these two together using SheepAspect mixin

[SingletonAspect]
public class SmtpAspect
{
	[SelectTypes(typeof(Customer))]
	public void Customer(){}

	[DeclareMixin("Customer")]
	public IEmailable CreateCustomerEmailable(Customer customer)
	{
		return new CustomerSmtpEmailable(customer);
	}

	private class CustomerSmtpEmailable: IEmailable
	{
		private readonly Customer _customer;

		public CustomerSmtpEmailable(Customer customer)
		{
			_customer = customer;
		}

		public string EmailAddress { get; set; }

		public void SendEmail(string subject, string message)
		{
			/* Just pretend this code is going to an SMTP server, not to a console */
			Console.WriteLine("To: {0} {1} ({2})", _customer.FirstName, _customer.LastName, EmailAddress);
			Console.WriteLine("Subject: {0}", subject);
			Console.WriteLine("\nMessage:");
			Console.WriteLine("Hola {0},", _customer.FirstName);
			Console.WriteLine(message);
			Console.WriteLine("\nSincerely yours");
			Console.WriteLine("Sheep");
		}
	}
}

With this aspect in place, your Customer class now magically implements IEmailable interface. The invocation will be directed to the implementation we have provided in our aspect (CustomerSmtpEmailable), which contains the infrastructure code of sending an email through an SMTP server or background tasks. Now you can actually cast your Customer arbitrarily to an IEmailable:

var customer = new Customer {FirstName = "Henders", LastName = "Sheep"};

var emailable = (IEmailable) customer; // Casting a Customer into IEmailable!

emailable.EmailAddress = "henders.sheep@foo.com";
emailable.SendEmail("Buy now!",
    "We have a great collection of sheep for you to buy.\nHurry, while stock lasts!");

That actually works, and you did not get InvalidCastException. Instead you’ll get this little output on your console window:

image

All source-code in this example can be found in SheepAspect.Example project on SheepAspect code repository.

Binding Mixins to Fields

Instead of declaring Mixin implementation factory method, you can also bind your Mixin implementations on aspect fields. For instance:

[SingletonAspect]
public class SmtpAspect
{
	[DeclareMixin("CustomerTypePointcut")]
	public IEmailable _customerEmailable;
}

Now your Customer will implement IEmailable interface that directs its execution to the IEmailable instance held by this _customerEmailable field on your SmtpAspect. Swapping the implementation of your Customer’s IEmailable is as simple as setting the value of this field.

Dependency Injection: Reborn

Some time in our career, we have learnt about separation of concerns, about how you should split domain-code from infrastructural concerns. Usually this is achieved by means of dependency-injection, the goal of which is to promote loose-coupling, maintainability, and testability. Dependency-injection has become one of the most fundamental principle in OO application development. But this is a principle that might no longer be relevant in AOP world.

The premise of dependency-injection does not necessarily hold true in AOP development process. There is no longer much need of rearchitecting your classes to fit into dependency-injection scheme just to achieve separation of concerns. You can in fact just implement your infrastructural concerns directly as part of your Customer domain class. For typical CRUD applications, you can do away without repository classes: you just simply inject ActiveRecord capability straight onto your entities through mixin.

Feeling dirty? Not quite. Separation is still maintained because these implementations are actualised as separate mixins that are injected externally by aspects where you consolidate all your infrastructural plumbing.

In our Customer/IEmailable example, neither your Customer class, IEmailable interface, nor their consumers are aware of any SMTP implementation. They are pure POCO. Then somewhere completely external to the core of your application code, you just introduce SmtpAspect mixins aspect where you actually implement your SMTP plumbing, and will statically enhance your customer class with SMTP capability. But from code perspective, your Customer class remains a simple POCO model.

For unit-testing, you can mock SmtpAspect class, and reimplement the IEmailable behavior with mocked behaviors.

Yes you do violate dependency-injection principle, you sinfully commit God-Object anti-pattern, but hey, those wisdoms was invented before the birth of AOP into static languages.

SheepAspect Preview 3

Filed under: Software Development — Hendry Luk @ 1:43 am
Tags: ,

This is not a massive release or anything. The idea is to push new changes rapidly to the public to attract early feedbacks, especially because this Preview3 release comes with a good set of new features and improvements that I genuinely feel very excited about. Many of these changes are based on feedbacks I have received from early adopters. So thanks to everyone who has shared their inputs.

What’s New

There is no significant changes on the underlying SheepAspect framework itself. This release is more of implementing new functionalities and natural progression of the feature-set of the library. Some of these new features are:

  1. The most significant new feature in Preview3 release is the Mixins support. With the new [DeclareMixin] advice, you can now statically add interface implementations to existing classes. I will write have written the details of this in a separate post.
  2. We also now have [DeclareAttributes] advice attribute. With this advice, you can statically add attributes to existing types, methods, properties, fields, and constructors. This will also be covered in the upcoming posts.
  3. IAspectAware interface, so you can now intercept when your aspects get instantiated by AspectRuntime so you can execute some initialisation routine. Again, this will be covered in the upcoming posts.
  4. A more complete set of pointcut implementations. Preview-2 advices could only target methods, properties, and instructions (method-calls, field gets/sets). In preview-3, they can also target types, constructors, fields, and property getters/setters. Events are only partially supported at the moment.
  5. Intellisense documentation. I understand that SheepAspect has a very steep learning curve. It can be overwhelming for users to get started or to figure out what each attribute does. In this Preview 3 release, most of SheepAspect’s public API is well documented.
    image10image9
  6. In Preview 2, SheepAspect required pdb files of all target binaries (so that SheepAspect can preserve debuggability of your weaved binaries). This has caused a problem for users who do not have pdb files of their target binaries. In Preview 3, pdb files are optional.

Get It Now

As usual, SheepAspect Preview3 is installable from NuGet: https://nuget.org/packages/SheepAspect. SheepAspect-Query-Analyzer for Preview-3 version is still due to come soon, and will be downloadable separately on the SheepAspect homepage.

July 14, 2011

SheepAspect Preview 2!

Filed under: Software Development — Hendry Luk @ 12:16 pm
Tags: ,

I’m very excited to announce the second preview release of SheepAspect. It’s available now, and you can install it to your project from NuGet gallery.

There are few new features introduced on this release, such as changes around its API, introducing more simplicity, bug fixes, and improved implementations. But there are few things that I’d like to highlight what’s new in this release.

NuGet Package

Previously, installing SheepAspect has always been a bit of a mess. You had to reference few libraries into your project, copy some compiler.exe into a folder, modify your csproj manually to include the compiler into your build-task, and pray all the stars are lined up in a perfect angle.

Now in Preview-2, NuGet will do all the mundane heavy-lifting for you. The only step you need to do now is to NuGet SheepAspect straight to your main project, and that’s it. Everything will be configured for you, and you’ll be all set to go before you finish reading this sentence.

When you NuGet SheepAspect into your project, there are 4 things that happen:

  1. It downloads all binaries and compiler into your project, and attach the necessary runtime libraries into your project references.
  2. It modifies your .csproj/.vbproj file to hook SheepAspect compiler into your post-build task.
  3. Generates a default configuration file (SheepAspect.config) and add to your project. This file has been pre-configured to just work immediately, which you can then use as a starting point to configure your SheepAspect settings. (More about this below).
  4. You’ll also be given one sample aspect class (/Aspects/SampleAspect.cs), and all it does is trace-logging around all public methods within your current project. (Entry, exit, exceptions).

The included sample-aspect is readily active and all fully functioning out-of-the-box, so that as soon as you nuget SheepAspect into your project and hit run immediately, you’ll watch all your public methods are now writing log traces to your debug window. From here, you can use this sample ‘SampleAspect.cs’ as a template to get started to write your very own first aspects.

SheepAspect.config

Another new feature introduced in Preview-2 is an easier configurability. So instead of tinkering with your .csproj/.vbproj msbuild tasks to configure SheepAspect compilation settings, you can now do so in SheepAspect.config. When you install SheepAspect, you’ll get a default config:

<?xml version="1.0" encoding="utf-8" ?>
<sheepaspect-config xmlns="urn:sheepaspect-config-1.0">
  <aspects>
    <assemblies>
      <include>YourProject.exe</include>
    </assemblies>
  </aspects>
  <weave>
    <assemblies>
      <include>YourProject.exe</include>
    </assemblies>
  </weave>
</sheepaspect-config>

It’s pretty straightforward. The xsd file is included so you can use Visual Studio intellisense to view all the different settings, although it’s all pretty basic at this point.

Build Report

SheepAspect compiler task spawns a separate (console) process to get around Visual Studio annoying tendency to lock dll files that you access during the task. That’s why in Preview-1 you could briefly see a console window popping up during compilation process. When an error occurs during the compilation, you’ll need to read the report printed on the console window.

The new SheepAspect is still using the same technique, but it’s more refined and well hidden. You won’t see any window popping up, and your compilation results will be reported nicely on your Visual Studio.

SheepAspect Query Analyzer

I have announced SAQA before, but this is the first time it has ever been released. SAQA’s releases will roughly follows the release cycle of SheepAspect, but it’s distributed as a separate download. You can download SAQA for SheepAspect Prevew-2 here on the project website.

Factory Per Aspect

Aspect factory determines how your aspect classes are instantiated during runtime. In the previous version, aspect factory was defined globally.

AspectFactory.Current = new StructureMapAspectFactory(structureMapContainer);

But there are times when you need to use a specific factory for your particular aspect. For example to instantiate your aspect from attribute declaration, or to instantiate from web-request (model-binding) or client cookie. New in Preview-2, now you can define your factory on a per-aspect basis.

[Aspect]
[AspectFactory(typeof(AttributiveAspectFactory))]
public class MyAspect
{
}

If no particular factory specified for your aspect, SheepAspect will resort to a global default factory.

AspectRuntime.Provider.DefaultFactory = new StructureMapAspectFactory(structureMapContainer);

That’s right, you’ve just seen AspectRuntime there. It’s a new addition in SheepAspect Preview-2 that gives you access to the runtime environment of your aspects. You can, for example, switch off all your aspects (or a specific aspect) for unit-testing purpose, or even to replace your aspect with a mock. You can inquire the lifecycle of your specific aspect, or even change them (e.g. from Singleton to PerThis). You can ask for the instance of a specific aspect for a particular jointpoint, and so on. It gives you a flexible and dynamic control upon your aspect-orientated runtime behaviors that can’t be scripted during compile-time alone.

SheepAspect

This is also the first release since the project changed its name from SheepAop. So if you’re using Preview-1, the upgrade path to Preview-2 will be a breaking and rocky one. But I’m very excited about this release, I’d strongly urge to to upgrade if you’re already on Preview-1, or to start trying out and play around with SheepAspect if you haven’t. So go on, it’s only a NuGet away ;)

These preview releases have always been about giving people an early access to gather as much feedback as I can. So any comments/issues/suggestions, as always, would be greatly appreciated :)

[More about SheepAspect]

June 27, 2011

SheepAspect Query Analyzer

Filed under: Software Development — Hendry Luk @ 3:27 am
Tags:

SheepAspect-Query-Analyzer (aka SAQA, not to be confused with Sucka’) is a simple tool I built to let you quickly punch in your SAQL pointcuts, and execute them immediately against your assembly files to get instant results.

The GUI is quite plain and basic, but it does the job.

This tool will hopefully come in handy for developers to get into SAQL pointcuts and syntax. You just simply punch in your pointcut attributes on the text editor as you normally would on C#. E.g.

[SelectMethods(“Name:’Get*’ & Public”)]

You will then get instant feedbacks from this pointcut by executing it against your assemblies, which will bring back all the jointpoints it picks up (i.e.: all public methods with names starting with ‘Get’). Since it takes standard syntax of C# attributes, once you’re happy with the result, you can just copy this line straight onto your C# class.

You can type in as many pointcuts as you wish on the text-editor. You can also define aliases using ‘=’. E.g.:

[SelectMethods(“Name:’Get*’ & Public & InType:@NhConfigTypes”)]
NhConfigTypes = [SelectTypes(“Namespace: ‘NHibernate.Cfg*’”)]

Those pointcuts are equivalent to the following code on the actual C# class:

[Aspect]
public class MyAspect
{
   [SelectMethods("Name:'Get*' & Public & InType:@NhConfigTypes")]
   public void Pointcut1() {}

   [SelectTypes("Namespace:'NHibernate.Cfg*'")]
   public void NhConfigTypes(){}
}

SAQA is available for download from the latest SheepAspect trunk. SAQA will also be included as part of the next SheepAspect release.

What’s next?

  • Keyboard shortcuts
  • Ability to save the progress of your SAQA workspace
  • Ability to view and open the source-code of your joint-points in Visual Studio
  • SAQA will be able to read from SheepAspect configuration file (that will be introduced, hopefully, in the next SheepAspect major release)
  • I have NO plan to deliver any intellisense functionality, but feel free to send me a patch

June 10, 2011

SheepAop Is Now SheepAspect.org

Filed under: Software Development — Hendry Luk @ 12:17 am
Tags:

I admit I didn’t spare much thought into the name when I started the project. I just simply followed the infamous Luk’s rule of project naming: “append the word ‘Sheep’ with something boringly obvious about the project”. Hence SheepAOP. It was just a random codename I picked for a project that was intended at that time as my own personal proof-of-concept on one rainy weekend. And I went slightly overboard with it without bothering myself to change to a friendlier name, and the name “SheepAop” managed to see the light of day.

It’s not a friendly name, and quite a mouthful to say, thanks to the double vowels in the acronym “AOP”. I’ve lately started develeoping an involuntary cringing habit on the sound of “SheepAOP” produced by my mouth every time I talk to people. And I hate cringing. Something has to be done about it. Changing my habit is a bit complicated, so instead I renamed the project.

So it’s official, the project is now called SheepAspect. It has found a lovely home at SheepAspect.org, and the source-code has been refactored to embrace the new name (which is obviously a breaking change). The old url (sheepaop.codeplex.com) still works fine and simply redirects you to the new address. So once again, say hi to SheepAspect, please help it feel comfortable with its new name.

June 6, 2011

SheepAop Part 5: Aspects Inheritance And Polymorphism

Filed under: Software Development — Hendry Luk @ 4:31 pm
Tags:

This Series

  1. Getting Started with SheepAOP
  2. Pointcuts and SAQL Basics
  3. Aspects Lifecycles & Instantiations
  4. Integrating with IoC containers
  5. Aspects Inheritance & Polymorphism
  6. Attributive Aspects (ala PostSharp)
  7. Unit-testing your aspects
  8. Extending SheepAop

Reusing Advices

Think back to the concurrency example from our previous post. We used the ReadWriteLockAspect to define the reading and writing operations within our ShoppingCart, and apply a read/write locking pattern onto it. Surely we can apply this same pattern to other classes where a safe concurrent access is required. We can refactor this well-tested concurrency pattern into an abstract aspect that we can reuse instead of reimplementing it when we need it again.

This gives us a perfect situation to demonstrate SheepAop’s modularity and reuse, as we refactor our ReadWriteLockAspect example into the following.

[AspectPerThis("ReadingsPointcut", "WritingsPointcut")]
public abstract class ReadWriteLockAspect
{
   protected abstract void ReadingsPointcut(); // Abstract pointcut
   protected abstract void WritingsPointcut(); // Abstract pointcut

   protected abstract int GetTimeout(); // Abstract method

   private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

   [Around("ReadingsPointcut")]
   public object LockRead(JoinPoint jp)
   {
      try
      {
         _lock.EnterReadLock(GetTimeout());
         return jp.Execute();
      }
      finally()
      {
         _lock.ExitReadLock();
      }
   }

   [Around("WritingsPointcut")]
   public void LockWrite(JoinPoint jp)
   {
      try
      {
         _lock.EnterWriteLock(GetTimeout());
         jp.Execute();
      }
      finally()
      {
         _lock.ExitWriteLock();
      }
   }
}

Now to apply a write/lock behavior to our ShoppingCart class, we simply extend the aspect. We just need to provide the implementation of the pointcuts. The behavior of the advice will be inheritted from the base aspect.

public class ShoppingCartLockAspect: ReadWriteLockAspect
{
   [SelectMethod("'* ShoppingCart::Get*(*)'")]
   [SelectPropertyGet("'* ShoppingCart::*'")]
   protected override void ReadingsPointcut(){}

   [SelectMethod("InType:'ShoppingCart' && Name:'AddProduct'|'RemoveProduct'")]
   protected override void WritingsPointcut(){}

   protected override int GetTimeout()
   {
      return 2000; // Timeout is 2 seconds
   }
}

Now we have used the base ReadWriteLockAspect to transform the ShoppingCart class to be thread-safe. We can reuse this same base aspect to any other target class that you intend to make thread-safe.

Tips on Pointcut Compositions: make sure you take advantage of the composability of SheepAop pointcuts when designing a reusable aspect. You can devide your pointcut into fragments to extract any reusable pattern out, and only expose a small fraction of it as abstract. As an example, our ReadWriteLockAspect can be modified to automatically apply the locks on all read/write methods and properties on the particular type that you target.

[AspectPerThis("ReadingsPointcut", "WritingsPointcut")]
public abstract class ReadWriteLockAspect
{
   [SelectMethod("'InType:@TargetClass && Name:'Get*'|'Is*' && !ReturnsVoid")]
   [SelectPropertyGet("InType:@TargetClass")]
   protected void ReadingsPointcut(){}

   [SelectMethod("InType:@TargetClass && Name:'Add*'|'Remove*'|'Set*'")]
   [SelectPropertySet("InType:@TargetClass")]
   protected void WritingsPointcut(){}

   //This is a small fragment of the pointcut that we expose as abstract
   protected abstract @TargetClass();

   /* and all the rest of it unchanged.. */
}

Now our base aspect provides a reusable template for our pointcuts, where a small fragment @TargetClass is left out as abstract to be composed to form the complete pointcut. The concrete aspect only needs to provide the implementation of this small fragment, rather than the whole pointcut expressions.

public class ShoppingCartLockAspect: ReadWriteLockAspect
{
   [SelectTypes("'ShoppingCart'")]
   protected override void TargetClass(){}

   protected override int GetTimeout()
   {
      return 2000; // Timeout is 2 seconds
   }
}

Other example

The ActorAspect example can also be refactored into an abstract class to reuse its advice behavior, but I will leave that as an exercise for the reader.

Reusing Pointcuts

In the previous example, we have used aspect inheritance to reuse a common advice behavior (read/write locking behavior) repeatedly for multiple pointcuts. Aspect inheritance can also be used to reuse common pointcut expressions repeatedly in multiple advice implementations. A common example of this is aspects for defining extensibility points in a plugin architecture.

For instance, consider you want to define an extensibility point that is triggered at the point of every bank-account transaction. So we define the following abstract aspect.

[Aspect]
public abstract class AccountTransactionPlugin
{
   [SelectMethod(@"InType:AssignableTo:'AccountBase'
            && Name: 'Withdraw'|'Deposit'
            && ArgTypes:'Money'")]
   protected void TransactionPointcut(){}

   [Around("TransactionPointcut")]
   public void AroundTransaction(MethodJointPoint jp)
   {
      OnTransacting((Account)jp.This, (Money)jp.Args[0], ()=> jp.Execute());
   }

   protected abstract OnTransacting(Account account, Money amount, Action proceed);
}

Now that we have this extensibility point defined, a third party can provide their plugins by implementing this base-class. For example, the following plugin will add a business policy to limit transaction amounts within our banking system.


public class TransactionLimitPlugin: AccountTransactionPlugin
{
   private const int CompanyLimit = 100000;
   private const int PersonalLimit = 2000;

   protected override OnTransacting(Account account, Money amount, Account proceed)
   {
      var limit = account.Customer.IsCompany? CompanyLimit: PersonalLimit;
      if(amount > limit)
         throw new TransactionOverlimitException(amount, limit);
      proceed();
   }
}

More plugins for this specific extensibility point can be added to the system by writing more implementations of AccountTransactionPlugin (e.g. a plugin to apply transaction-fees depending on the types of the account). Typically we would have a set these abstract aspects with different pointcut-expressions, each represents different extensibility point within the system (e.g. when an order is created, when a case is raised, when a conversation occurs, etc), onto which a specific plugin can be hooked and unhooked. You can jazz up this technique by adding MEF to the mix.

Note that the domain code of our application might not be designed with plugin architecture in mind. It probably does not even aware of any extensibility system that is in place. This brings us to our next point…

Speaking of Extensibility..

Extensibility has always been a chicken-and-egg problem. Many business applications require the extensibility to accomodate rapidly changing business rules, policies, and workflows, sometimes allowing extensibility by third-party developers. To achieve this, architects are often faced with underdesign/overdesign issue. If you underdesign, you may have to make massive changes later in the development cycle. If you overdesign with excessive amount of extensibility point on every method of your application, the implementation may be burdened with code of questionable usefulness.

With AOP, you can delay making design decisions for future requirements because you can implement those extensibility points unobtrusively using aspects. You can focus on the current requirement of the system.
In this respect, AOP works in harmony with YAGNI (You aren’t gonna need it). Implementing a feature just because you may need it in the future often results in wasted effort because you won’t actually need it. With AOP, you can stick faithfully to your current requirement, and if you do need a particular kind of functionality later, you can implement it without having to make system-wide modifications

Attribute-based Aspects (ala Postsharp)

(Since this question gets asked quite a lot, I will post this section again in a separate post as a handy reference)

SheepAop is not specifically built to support attribute-based declarative approach used in Postsharp. But using the rich pointcut expressions and aspect inheritance in SheepAop, it’s quite trivial to implement our own implementation. We just simply need to write this simple abstract aspect to deliver the same attribute-based functionality.

[Aspect]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public abstract class AroundMemberAttribute: Attribute
{
   [SelectMethods("HasCustomAttributeType:ThisAspect")]
   [SelectPropertyGets("HasCustomAttributeType:ThisAspect")]
   [SelectPropertySets("HasCustomAttributeType:ThisAspect")]
   protected void DecoratedPointcut(){}

   [Around("DecoratedPointcut")]
   public abstract object Around(JointPoint jp);
}

This aspect simply defines a pointcut that matches all methods and properties that are decorated using the concrete type of this aspect. So now we can use this base attribute to create our TransactionalAttribute as an example.

[Aspect]
public class TransactionalAttribute: AroundMemberAttribute
{
   public override object Around(JointPoint jp)
   {
      using(var tx = new TransactionScope())
      {
         var result = jp.Execute();
         tx.Complete();
         return result;
      }
   }
}

That is all it. Now you can use this attribute to decorate methods and properties that you want to make transactional. For instance:

[Aspect]
public class OrderService: IOrderService
{
   [Transactional]
   public void ProvisionOrder(Order order)
   {
      // .... whatever ...
   }
}

For your convenience, the base-aspect AroundMemberAttribute is included straight out-of-the-box within SheepAop, so you don’t have to write it every time. Another variation of the base attribute is called AroundCallAttribute. They both have equivalent functionality, but whereas AroundMemberAttributes targets the bodies of methods and properties decorated with the attribute, AroundCallAttribute targets the calling of methods, getting/setting properties, and reading/writing fields decorated with the attribute.

Summary

Inheritance is a key component in making reusable aspects in SheepAop. Since SheepAop aspects are just normal POCO classes, you can make use of the same Object-Oriented techniques such as inheritance and polymorphism that we’re all familiar with. The same techniques are also used to implement the declarative attribute-based aspects that gives a lot of convenience for many simple cases.

May 31, 2011

SheepAOP Part 4: Integrating with IoC containers

Filed under: Software Development — Hendry Luk @ 5:02 am
Tags: ,

This Series

  1. Getting Started with SheepAOP
  2. Pointcuts and SAQL Basics
  3. Aspects Lifecycles & Instantiations
  4. Integrating with IoC containers
  5. Aspects Inheritance & Polymorphism
  6. Attributive Aspects (ala PostSharp)
  7. Unit-testing your aspects
  8. Extending SheepAop

Aspect Factory

In the previous post, we have discussed at great length about various SheepAop lifecycles and how they affect the specific points at which SheepAop should instantiate new aspect objects. Whenever your lifecycle strategy determines that a new aspect needs to be created, by default SheepAop will look for a default parameterless constructor in your aspect class.

SheepAop lets you to can change this behavior and use your own IoC container to manage the instantiations of your aspects and wire up their dependencies. This is done by implementing your own custom AspectFactory. For example, the following code will hook SheepAop with StructureMap.

public class StructureMapAspectFactory: AspectFactory
{
   private readonly Container _container;
   public StructureMapAspectFactory(Container container)
   {
      _container = container;
   }

   public override object CreateInstance(Type type)
   {
      return _container.GetInstance(type);
}
}

And you add the following code to the entry point of your program (i.e. Global.asax in a web application)

AspectFactory.Current = new StructureMapAspectFactory(container);

All pretty straightforward. Now if you take the example aspect class from few posts back…

[Aspect]
public class PurchasingNotificationAspect
{
   private readonly INotificationService _notifier;
   public PurchasingNotificationAspect(INotificationService notifier)
   {
      _notifier = notifier;
   }

   [SelectPropertySets("Name:'StockQty' & InType:Name:'Product'")]
   public static void SettingProduct(){}

   [Around("SettingProduct")]
   public void CheckStockForNotification(PropertySetJoinPoint jp)
   {
      jp.Proceed();
      if ((int)jp.Value < 5)
      {
         _notifier.Send(Notice.Restock, (Product)jp.This);
      }
   }
}

… StructureMap will now handle the instantiations of this aspect class, including the wiring of the INotificationService dependency via the constructor.

I highly recommend to always register all your aspect classes using transient configuration on your IoC container, and to leave it to SheepAop to handle the lifecycle management of the instances.

May 17, 2011

SheepAOP Part 3 – Aspect Lifecycles

Filed under: Software Development — Hendry Luk @ 8:23 am
Tags:

This Series

  1. Getting Started with SheepAOP
  2. Pointcuts and SAQL Basics
  3. Aspects Lifecycles & Instantiations
  4. Integrating with IoC containers
  5. Aspects Inheritance & Polymorphism
  6. Attributive Aspects (ala PostSharp)
  7. Unit-testing your aspects
  8. Extending SheepAop

This Post

In this post, I will introduce you with the notion of aspect-lifecycle in SheepAop, a feature that I have claimed more than once to be among the utmost importance in an AOP framework for taking AOP’s real-world use beyond the highly clichéd logging and transaction toy-demo. Aspect-lifecycles offer some elegant answers to a whole array of real-world problems that are not normally solvable using conventional AOP solutions.

This post will take you through a generous amount of those examples to demonstrate some real-world applications of AOP in general, and of aspect-lifecycles in particular. No more Logging, Transaction, or SecurityAuthorization demo, I believe we’ve had enough of that :)

Overview

By default, SheepAop uses Singleton lifecycle, which means that only one instance of an aspect type exists. This is the simplest form of aspect instance. The state of a Singleton aspect is effectively global. Usually this arrangement is well suited for stateless aspects, as well as aspects with an inherently global state, such as caching or resource-pool.

But in some situations, especially when you are creating reusable aspects, you want to associate the aspect’s state in a very specific manner (e.g. per individual object, or a particular class, or per scope of a control flow).
The aspect-lifecycle mechanism in SheepAop offers various ways to control the lifecycles of aspect instances, and offers many interesting and powerful design choices.

There are several kinds of aspect lifecycles in SheepAop:

  • Singleton (default)
  • Transient
  • Per object (PerThis and PerTarget*)
  • Per control-flow
  • Per type*

*) Not yet supported

Note: It’s very trivial to implement your own aspect-lifecycle, e.g. WebRequestLifecycle, or WebSessionLifecycle. We’ll cover that on “Extending SheepAop” later in the series.

Singleton Lifecycle (Default)

Singleton-lifeycle is in effect when you don’t specify any lifecycle in your aspect class declaration.

[Aspect]
public class MyAspect
{
}

You can also declare it explicitly using SingletonAspect, which makes no difference whatsoever.

[SingletonAspect]
public class MyAspect
{
}

Let’s write a simple toy program that creates 2 Account objects and calls their methods.

public class MyProgram
{
   public static void Main()
   {
      var account1 = new SavingAccount(12345);
      var account2 = new SavingAccount(77777);

      account1.Credit(200);
      account1.Debit(100);

      account2.Credit(200);
      account2.Debit(100);
   }
}

And apply some aspect:

[Aspect]
public class MyLoggingAspect
{
   var _logger = logger;
   public MyLoggingAspect(ILogger logger)
   {
      _logger = logger;
      _logger.Trace("> Creating MySingletonAspect instance");
   }

   [SelectMethods("'void SavingAccount::*(System.Int32)'")]
   private void AccountPointcut();

   [Around("AccountPointcut")]
   pulbic void AroundAccountMethods(MethodJoinpoint jp)
   {
      _logger.Trace(">> Calling {0}({1}) on {2}", jp.Method, jp.Args[0], jp.This);
      jp.Execute();
   }
}

PS: The ILogger parameter on the constructor is injected by your IoC container. We’ll look into how to hook SheepAop with your IoC container in the next post or two.

The output is:

> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{12345}
>> Calling Debit(100) on SavingAccoung{12345}
>> Calling Credit(200) on SavingAccoung{77777}
>> Calling Debit(100) on SavingAccoung{77777}

Only one MySingleAspect instance will ever be created over the course of the program, which in this case also means that you will always append all log entries to only one ILogger instance.
(All previous examples in the series so far use Singleton lifecycle by default, so I won’t bother you with another example on Singleton aspects).

Transient Lifecycle

Transient lifecycle will create a new aspect instance for every time a join-point is hit.
Let’s use the same toy program from the example above, and just slightly change the aspect declaration to the following:

[TransientAspect]
public class MyLoggingAspect
{
   /* unchanged */
}

Now the output will become like the following:

> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{12345}
> Creating MyLoggingAspect instance
>> Calling Debit(100) on SavingAccoung{12345}
> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{77777}
> Creating MyLoggingAspect instance
>> Calling Debit(100) on SavingAccoung{77777}

A new MyTransientAspect instance is created for every join-point hit, which will append a log entry to a potentially different ILogger instance each time.
There really are not many realistic scenarios where Transient lifecycle would be necessary, but it can be useful especially when you want to make use of the lifecycle-capability provided by your IoC container. More about IoC integration in the next post.

Per Object Lifecycles (PerThis and PerTarget)

If an aspect A is defined AspectPerThis(Pointcut), then one object of type A is created for every instance that is the executing object (i.e., “This”) at any of the join points picked out by the Pointcut.
The advice defined in aspect A will run only at a join point where the currently executing object has been associated with an instance of A.

So if we modify our first toy (logging-aspect) example as follows:

[PerThis("AccountPointcut")]
public class MyLoggingAspect
{
   /* unchanged */
}

.. the output will become:

> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{12345}
>> Calling Debit(100) on SavingAccoung{12345}
> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{77777}
>> Calling Debit(100) on SavingAccoung{77777}

Everytime the “AccountPointcut” hits a join point, SheepAop will look at the object bound to This (the current SavingAccount instance), and create a new aspect instance if there is not already a MyLoggingAspect in existence for that SavingAccount instance. The aspect instance is eligible for garbage collection at the same time as the object it is associated with.

The PerTarget model works in a very similar manner to PerThis. If an aspect A is defined AspectPerTarget(Pointcut), then one object of type A is created for every object that is the Target object of the join points picked out by Pointcut. The advice defined in A will run only at a join point where the target object has been associated with an instance of A.
Note: Target object is only applicable to join-points matched using call-pointcuts, not member-pointcuts. Therefore consequently only call-pointcuts can be used with PerTarget lifecycle.

Example: Concurrent Object

In this example, we will create an aspect to convert your non-thread-safe class into a thread-safe one by applying the read-write lock pattern, hence allowing concurrent access to the objects. This pattern requires one lock for each object instance. The per-object lifecycles provide a mechanism to associate a new aspect isntance with each execution (‘This’) or ‘Target’ object.

In the following example, we will use AspectPerThis lifecycle to associate each of your ShoppingCart objects with a new ReadWriteLockAspect.

[AspectPerThis("Readings", "Writings")]
public class ReadWriteLockAspect
{
   [SelectMethod("'* ShoppingCart::Get*(*)'")]
   [SelectPropertyGet("'* ShoppingCart::*'")]
   protected void Readings(){}

   [SelectMethod("'void ShoppingCart::*(*)'")]
   [SelectPropertySet("'void ShoppingCart::*(*)'")]
   protected void Writings(){}

   private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

   [Around("Readings")]
   public object LockRead(JoinPoint jp)
   {
      try
      {
         _lock.EnterReadLock();
         return jp.Execute();
      }
      finally()
      {
         _lock.ExitReadLock();
      }
   }

   [Around("Writings")]
   public void LockWrite(JoinPoint jp)
   {
      try
      {
         _lock.EnterWriteLock();
         jp.Execute();
      }
      finally()
      {
         _lock.ExitWriteLock();
      }
   }
}

Note: Ideally, given that the read/write lock pattern itself is reusable, you would define this aspect as abstract (with abstract pointcuts) so you can reuse the aspect. We will cover this in part 5 (“Aspect Inheritence”).

So now each of your ShoppingCart instance will be assigned with a ReadWriteLockAspect, which will lock and release read/write access to your object. We have just converted our ShoppingCart to be thread-safe without introducing any additional noise into your actual code.

Examples: Timing-Out DbConnection Instances

In this example, we will write an aspect to limit the duration for which each DbConnection instance may keep its connection open. We’ll set a timer to forecfully close DbConnection instances that have been left open for over a certain period of time. Since the timer is associated with each connection object, we’ll need an aspect with per-object lifecycle (in this case, PerTarget).

[AspectPerTarget("OpeningConnection")]
public class ConnectionTimeoutAspect
{
   [SelectCallMethods("'void System.Data.DbConnection::Open()'")]
   protected void OpeningConnection(){}

   [SelectCallMethods("'void System.Data.DbConnection::Close()'")]
   protected void ClosingConnection(){}

   private const int TIMEOUT = 1000;
   private Timer timer;

   [Around("OpeningConnection")]
   public void AroundOpeningConnection(JoinPoint jp)
   {
      timer = new Timer(_=> ((DbConnection)jp.Target).Close(), null, 0, TIMEOUT);
   }

   [Around("ClosingConnection")]
   public void AroundClosingConnection(JoinPoint jp)
   {
      timer.Dispose();
   }
}

Example: Actor Model

Ayende recently blogged about his Actor implementation. In this example, we’ll look at how to reimplement the solution using aspect-oriented approach. The idea is to enable actor capability on an arbitrary POCO object without modifying its codes.
The premise of an actor is that all non-read actions must be executed asynchronously (non-blocking), but these actions must be executed sequentially within each actor. Consider the following actions from Ayende’s example:

connection1.Send("abc");
connection1.Send("def");

I care for “abc” to be sent before “def”, and for both to be sent before anything else is sent through connection1. But I do NOT care if I have anything else sent between “abc” and “def” (through different connections).
Additionally, every read operation must be executed synchronously, while still maintaining the order of executions. (i.e. all read operations should execute after all previous queued actions have completed).

Since each actor object requires its own sequential tasks-queue, we need to associate an aspect with each actor instance. So let’s create an aspect with PerThis lifecycle to target our (synchronous) Connection class.

[AspectPerThis("NonReadActions")]
public class ActorAspect
{
   [SelectTypes("'Sheep.MyConnection'"]
   protected void TargetClass(){}

   [SelectMethods("Type:@TargetClass & !ReturnsVoid"]
   protected void ReadOperations(){}

   [SelectMethods("Type:@TargetClass & !@ReadOperations"]
   protected void NonReadActions(){}

   private readonly ConcurrentTaskQueue _queue = new ConcurrentTaskQueue();

   [Around("NonReadActions")]
   public void AroundActions(JoinPoint jp)
   {
      _queue.EnqueueToExecution(()=> jp.Execute());
   }

   [Around("ReadOperations")]
   public object AroundReading(JoinPoint jp)
   {
     using(_queue.EnterLock())
     {
         _queue.WaitTillCompleted();
         return jp.Execute();
     }
   }
}

Note: Similar to the previous example, this aspect should ideally be defined as an abstract aspect so we can reuse the actor pattern whenever it’s needed.

All the synchronous methods in our Connection class have now been fully converted to work synchronously, and follow the actor-model behavior that we desire. It’s all done without cluttering the actual Connection class with the complex plumbing of Actor pattern. We extract the pattern out into an aspect, leaving your original class clean from irrelevant bits of technical complexities.

Other Examples:

There are many other real world applications of PerThis and PerTarget lifecycles, including:

  • Dirty-tracking (tracks all changes of fields and properties within an object)
  • Lazy-Load proxy
  • Cache the output of a costly method that is called multiple times on the same object, e.g. fileStream.ReadToEnd()

Per-Control-Flow Lifecycle

If an aspect A is defined AspectPerFlow(Pointcut), then one object of type A is created for each flow of control of the join points picked out by Pointcut as the flow of control is entered. The advice defined in A may run ONLY at any join point within that control flow. An instance of the aspect is created upon entry into each such control flow.

Example: Context Passing

Consider you are developing a flight-booking application. As part of the booking process, the application will create flight itineraries objects.

var itinerary = new Itinerary(flight);

The constructor also automatically sets the CreatedDateTime property of the Itinerary object with the current system time.

Today you are requested to allow the system to be used by a travel-agent company who bulk their booking transactions in an end-of-day basis. So we provide them with the following client-facing API:

public void BookFlightFromTime(DateTime orderTime, Flight flight, Customer customer /* etc etc*/)
{
}

Note the first parameter “orderTime“. Being an end-of-day bulk operation, all Itinerary objects created within the scope of this method must have their CreatedDateTime properties assigned with the specified orderTime, rather than the current (end-of-day) system time.
Typically this requires passing the orderTime from the client, down through every method call that eventually leads to Itinerary creations. All programmers are familiar with the inconvenience of adding a method argument to a number of methods just to pass this kind of context information.

Using SheepAop, this kind of context passing can be implemented in a modular way. The following code adds an Around advice that runs when an Itinerary object is instantiated within the control-flow of BookFlightFromTime method.

[AspectPerFlow("BookFlightFromTimeMethod")]
public class OrderDateAspect
{
   [SelectMethod("void BookingClient::BookFlightFromTime(DateTime, *)")]
   protected void BookFlightFromTimeMethod(){}

   [SelectConstructor("InType: 'Sheep.Itinerary'")]
   protected void CreatingItenerary(){}

   private DateTime _orderTime;

   [Around("BookFlightFromTimeMethod")]
   public void BookFlightFromTimeMethod(MethodJoinPoint jp)
   {
      _orderTime = jp.Args[0];
   }

   [Around("CreatingItenerary")]
   public void AroundCreatingIntenerary(JoinPoint jp)
   {
      jp.CreatedDateTime = _orderTime;
   }
}

This aspect affects only a small number of methods, but note that the non-AOP implementation of this functionality might require editting many more methods, specifically, all the methods in the control-flow from the client to the Itenerary creation. This is one major characteristic of aspect-lifecycle control. While the aspect is short and affects only a modest number of benefits, the complexity the aspect saves is potentially much larger.

Example: Transactional Methods: The Right Way

Transaction is one of the most cliche example in aspect-oriented-programming examples, and I’ve promised I would not mention the word today. But this one is not your typical transaction AOP demo.
The typical implementations in most AOP demos are usually overly naive. To explain what I mean, let’s write a simple ordinary TransactionAspect (using default Singleton lifecycle)

[Aspect]
public class TransactionAspect
{
   [SelectMethods("'* Sheep.ApplicationService.*::*(*)' & Public")]
   private void TransactionalMethods(){}

   [Around("TransactionalMethods")]
   public object AroundTransactionalMethods(JoinPoint jp)
   {
      using(var tx = new Transaction())
      {
         var returnValue = jp.Execute();
         tx.Commit();
         return returnValue;
      }
   }
}

That was quite straightforward. But now consider if you have non-transactional operations within your methods, e.g. sending an email. You don’t want to send out the email if the transaction fails. Furthermore, if you apply automatic retries on your transaction aspect, you won’t want multiple duplicate emails sent out.
It’s usually hard to solve this problem using other AOP solutions that do not support any notion of lifecycle control.

We will modify our TransactionAspect above to intercept all calls to non-transactional methods, and defer their executions until at the end of the transaction (upon successful completion of the transaction).

[AspectPerFlow("TransactionalMethods")]
public class TransactionAspect
{
   [SelectMethods("'* Sheep.ApplicationService.*::*(*)' & Public")]
   private void TransactionalMethods(){}

   [SelectMethods("HasCustomAttributeType: 'Sheep.NonTransactionalAttribute' & ReturnsVoid")]
   private void NonTransactionalMethods(){}

   private Queue<Action> _nonTransactionalActions = new Queue<Action>();

   [Around("NonTransactionalMethods")]
   public void StealNonTransactionalMethods(JoinPoint jp)
   {
      _nonTransactionalActions.Add(()=> jp.Execute());
   }

   [Around("TransactionalMethods")]
   public object AroundTransactionalMethods(JoinPoint jp)
   {
      using(var tx = new Transaction())
      {
         var returnValue = jp.Execute();
         tx.Commit();

        foreach(var action in _nonTransactionalActions)
           action();
        return returnValue;
     }
   }
}

Now all executions of non-transactional methods will be intercepted, and stored to a queue to be executed later when the transaction is completed.
A non-AOP implementation of this solution is non-trivial. You would have to massively change the structure of your code to pull out all calls to non-transactional methods, move them to the end of the transaction, and somehow maintain the contexts that are required by those methods.

Summary

Aspect-lifecycle is a key feature in SheepAop that takes aspect-orientation from logging and transaction to a whole another class of far more complex real-world problems.
The ability to associate the state of an aspect to a specific program context allows rich yet elegant aspect-oriented solutions, often affecting only a modest number of areas, but would otherwise require potentally a significantly larger complexity, if possible at all, using conventional (singleton) AOP solutions.
Later in the series, we will explore how you can create your own aspect-lifecycle implementations, such as PerHttpSessionLifecycle, or PerHttpRequestLifecycle

May 16, 2011

SheepAop Road Map

Filed under: Software Development — Hendry Luk @ 8:37 am
Tags:

People have asked me what my next plans are for the project. So here’s a quick list of some works I got lined up for SheepAop, ordered by priority:

  1. Documenting and posting blog articles.
    Writing has always been, and will forever be, astronomically tedious and majestically mind-numbing. There has been zero coding activity in SheepAop for a good couple of weeks now since I started the documentation work; and it’s still nowhere near finish.
    Writing is depressing, often suicidal. That’s a universal truth. But an open-source project is only as useful as how much people know how to use it. So this has to be done as my utmost priority before I embark on any further development effort.
  2. Looking into all those NDepend inspection warnings and refactor accordingly. (I’ll blog about the whole process).
  3. SAQL Query Analyzer.
    This would be a fantastic tool to get a visual understanding on SheepAop pointcuts and various SAQL expressions. It will help the learning curve, and ultimately the adoption of SheepAop. One small catch: I’m technically daft and aesthetically hopeless at GUI design.
  4. CFlow pointcuts. Ability to use the control-flows of the program as part of your pointcut expressions. (e.g.: The pointcut of a dirty-tracking aspect should not match property-settings called from within the control flow of the object constructor). Based on AspectJ feature with the same name.
  5. Working on a few pointcut types, SAQL criteria, and aspect-lifecycles that are currently unimplemented (most importantly: the Constructor and Instantiation pointcuts)
  6. Add features to help unit-testing
  7. Support “ImplementInterface” advice
  8. Support “Mixin” advice

Quite a fat pipeline ahead. It will keep me busy for at least the next couple of months.

May 11, 2011

SheepAop Code Review with NDepend (Part I)

Filed under: Software Development — Hendry Luk @ 3:21 pm
Tags: ,

Now that SheepAop has reached its first milestone, I think it’s time to stop for a moment and review the code to discover potential code-smells before I engage any further development.
I’ll use NDepend for our code-review exercise today, and I’ll be doing it live while I’m reviewing the code, refactor, and write it down on this post on every step as I go along.

Dependency Graph

clip_image002[4]

The very first thing I did was to check the dependency graph to get an overall understanding about the structure of the code. To my surprise, the dependencies did not immediately look quite as obvious as the picture I had in my head.

A large part of the graph still fits very well with what I expected:

  1. There are some helper-classes and exceptions down at the bottom, which contain general-purpose classes that help streamline our contact with the outside world (.net collections, mono-cecil, etc). These helpers are common and should be completely agnostic to anything SheepAop, which is confirmed by this graph: there’s no line coming out from these modules.
  2. SheepAop.Aspects contains a set of out-of-the-box sample implementations of SheepAop aspects. They’re not part of the framework, so I don’t expect to see anything SheepAop to reference it. The graph agrees.
  3. SAQL is a DSL layer on top of SheepAop API. The core machinery of SheepAop should be completely agnostic of SAQL. The graph looks fine: all Saql namespaces appear to be left alone by the rest of the town.
  4. SheepAop is designed so that making new Advice implementations should be easy because it’s the one part in SheepAop that will change (and be added) the most. For that reason, all advice classes should be external to the core so that it’s easy to plug new ones. The graph seems to agree with that. There are currently only 2 advices in SheepAop: AroundAdvice and LifecycleAdvice, and the graph shows no arrow pointing to that direction. (Except the unexpected arrow from Pointcuts to AroundAdvising, which we’ll explore below).

Potential Problems

There are, however, few things that I was not categorically pleased. I’ll list them all as a note for now, even though I’m not sure whether or not they turn out to be code-smells. But we will investigate each one of them later on.

  1. I was surprised to see how much SheepAop.Compile namespace assumes such a central role in the whole architecture. Its responsibility is supposed to be limited to the compilation part of SheepAop. It should depend on other SheepAop components to do this task, but conversely the other components should NOT have much interest in the compilation business. The graph, however, shows that almost every part of SheepAop depends on the compiler, which doesn’t seem right.
  2. As mentioned above (in point#4), SheepAop Advice classes should be pluggable modules that adding new ones should be cheap and easy. I’m quite curious about why SheepAop.Pointcuts namespace has any hard reference to AroundAdvice.
  3. I also did not expect to see SheepAop.Core to depend on the Runtime component and Attributes. Attributes is supposed to provide users with a friendly interface to interact with the core SheepAop API. What’re the attributes possibly doing that makes them so critical even the core engine has to depend on it?
  4. Like Advices, Lifecycles needs to be a pluggable thing so that it’s easy to add new ones (I’m still developing some as we speak), as well as for users to make their own custom Lifecycles to be plugged to SheepAop. I was hoping not to see any hard reference from the Core to Lifecycles if I can help it. We’ll see what we can do.
  5. (After the first refactoring below) We have cyclic dependencies between Core<->Pointcut. We’ll investigate this. Probably Pointcut should really be part of the Core, considering how central the role it plays in SheepAop.

Investigation

Let’s now investigate each of them. I’ll start from the first one.

1. Everything -> SheepAop.Compiler

There are currently several dependencies toward the compiler which we want to get rid of. I’ll start with the most worrying one: the circular dependency between the Compiler and the Core.

clip_image003[4]

The one I need to look out for is the reference INTO the Compiler (greens and blacks). The answer seems quite obvious here: AdviceBase and AspectDefinition are defined as Compiler components. They are not. They have little to do with compilation. Let’s try moving these classes into the Core namespace. If they are indeed part of the Compiler, there will appear even more dependencies toward the Compiler coming from these classes. I’ll also need to bring along IAdvice and IWeaver since they are depended upon. So let’s try and refactor this.

clip_image005[4]

Great! The dependencies are now heading to one direction. All the unit-tests still pass. So this one is sorted. Let’s take a look on our next dependency on the Compiler.

Wait, they’re all gone!

clip_image006[4]

No more circular dependency on the Compile components (orange). It turns out that those 4 classes we just refactored were the culprit of all the heavy dependencies on the Compiler. As shown by the green blocks, the only components that now depend on our Compiler are our 2 Advices: AroundAdvice and LifecycleAdvice. That sounds perfectly correct. And the picture is starting to take its shape.

2. Pointcuts -> AroundAdvising

We want all Advices (AroundAdvice in this case) to be decoupled from the framework because it’s one part in SheepAop that will change (and added) the most, so it has to be easy to plug new Advices.

Same deal, I click on the line between Pointcuts and AroundAdvising to view the matrix. (It’s quite a tall picture so I cut out the irrelevant bits).

clip_image008

The green squares are the ones that shouldn’t be there. I wonder what they are. Let’s check the first one: PropertyPointcut.

clip_image010

That’s a method I found in the class. That’s a leftover method I inherit from the old version of SheepAop infrastructure (when Advice was still tightly coupled to the system). I don’t think they still contribute to the new SheepAop infrastructure. I checked the other green squares and they all seem to have the same garbage leftovers. Let’s get rid of them and run the unit-tests to make sure it doesn’t break anything.

clip_image011

Great, all the greens are cleared. All unit-tests still pass.

clip_image013

Now AroundAdvice is completely decoupled from SheepAop. The graph (centered on AroundAdvising) shows no green, meaning that there’s nothing in the whole SheepAop that depends on it (except our public Attributes). So this has been another success.

3.A SheepAop.Core <-> SheepAop.Runtime

There are several components that SheepAop.Core is dependent on, which it shouldn’t. Let’s start with the Runtime namespace. SheepAop Core should not depend on its Runtime components.

clip_image015

The green ones should not be there. It seems that they are all pointing to 2 method delegates (AdviceInvoker and AdviceCallback at the top), all of which are coming from Joinpoint classes. It makes me feel that those 2 delegates are integral parts of SheepAop Joinpoint system. Maybe I should put them together with the Joinpoint. In fact, I start to feel that these objects warrant their own separate namespace ( “Joinpoint”, instead of piggybacking the “Core” namespace). But first thing first.

clip_image017

Great. Now let’s split all those Joinpoint classes to a separate namespace, and hope that the resulting namespaces won’t cause cyclic dependencies.

clip_image019

Wow, this is unexpected. Not only that Core and Joinpoint did not end up in a cyclic dependency, there is in fact no dependency at all between them! They are both unrelated. It really was, after all, a good decision to split them to separate namespaces. But now it makes me wonder about the name. What’s with the name “SheepAop.Core.Joinpoint” if it’s not related to the Core? Since Runtime is the biggest dependent of this namespace, I think I should merge it with Runtime. After all, a join-point is a SheepAop representation model to describe runtime snapshots. Ironically, it turns out that the 2 delegates we pulled out of Runtime namespace were actually at the right place after all.

clip_image021

OK, we’re good. Let’s move on.

3.B SheepAop.Core <-> SheepAop.Attributes

As explained at the start of this post, Attributes is a facade API for users to interact with SheepAop framework, and it should sit right at the edge, together with SAQL. So why does the Core depend on it?

image
Aha, a black one. SingletonAttribute extends LifecycleAttributeBase, but LifecycleAttributeBase has a static CurrentInstance that defaults to SingletonAttribute. A cyclic.

image

Generally I have no problem with this kind of cyclic dependency because it does not indicate a code smell. But in this particular case, LifecycleAttributeBase needs to be refactored anyway into an ILifecycleProvider interface which will fit better as a Core component. In general, attributes are intended to be used as user API. Doesn’t feel right to see it as a part of the Core engine. So yeah, I’ll refactor this, and hope that it will also solve the circular dependency.

image

It does. That was quite a big refactoring, but all the unit-tests still pass, so we’re good. We’re now on a much better state with this refactoring.
As shown by the green blocks, the only component that knows anything about SheepAop Attributes (orange) is now only the Compiler, which sounds fantastically correct. (SheepAop.Aspects, as mentioned earlier, is just a sample implementation of SheepAop. So just ignore it, it’s not part of the framework).

Notice that our refactoring has also cleared the cyclic dependency between the Core and Lifecycle, so we just unconsciously solved the first half of task#4.

4.A Sheep.Core.Lifecycle <-> Sheep.Core

(Done)

4.B Sheep.Aop.Lifecycle <-> SheepAop.Runtime

To reiterate, we want Lifecycle to be decoupled from the framework so that it’s easy to introduce new ones (which are being developed as we speak), and for users to add their custom ones.

image

Ah this one is easy. IMayHaveAspect is a very specific spare-part of the PerThis lifecycle, so it should really belong in the Lifecycle namespace. So let’s move it.

image

Ah looks much better now.

5. SheepAop.Core <-> SheepAop.Pointcuts

I have a strong feeling that Pointcut should be merged into the Core. But let’s see.

image

Ah it looks like the Core only knows Pointcut by IPointcut interface and PointcutBase. I think I can move IPointcut to be part of the Core, and refactor the reference to PointcutBase to use the interface instead. (It’s generally a good idea to program against interfaces anyway).

image

Sweet. The cyclic traffic is gone, and the dependency becomes unidirectional: SheepAop.Pointcut –> SheepAop.Core, which feels quite right. So this is our final result:

image

Now we can see no more direct cyclic dependency in our code. But I can’t convincingly tell whether there is any indirect circular dependencies in the graph.
The graph is messy. Now that the relationships between the components have changed substantially, I think we should rearrange our graph to better reflect the new shape of our architecture. (I’ll also hide helper classes and exceptions from the graph).

image

The modules are now arranged so that all dependencies flow from top to bottom. The structure of the architecture is now very visible from the graph. As you can see, all arrows are pointing downwards. Or in simple terms: there is no possible circular dependency between our modules, directly or indirectly.

Next Step

I think that’s it for today, I’m calling it a night. We have achieved quite a good result so far. Tomorrow (or whenever I got time), I will continue the review with NDepend Metrics and CQL code-inspections. I can see some warnings already on my NDepend panels indicating some problems detected by NDepend code-inspector. But I’m too tired for the day, so I’ll spare that for the next post.

All refactored code has been checked back in to the SheepAop repository. You can check it out if you want to explore the result of today’s refactoring.

Next Page »

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.