Friday, June 26, 2009

AOP - Why should you care?

Here's a snippet that, as much as I hate to admit it, resembles some code that I've been known to write:

public class BusyBodyService
{
    public Object fetch() throws Exception
    {
        // trace entry
        log("Entered BusyBody");

        // start profiler
        startStopWatch();

        // check cache
        if (isCached())
        {
            return fetchFromCache();
        }

        // fetch from database - loop until connection made or bail out
        int numAttempts = 0;
        int maxRetries = 5;
        Exception t = null;
        Object retval = null;

        do
        {
            numAttempts++;

            // set isolation level
            setRepeatableRead();

            // start transaction
            startTransaction();

            try
            {
                // -------------- pure business logic starts here -------------- 
                retval = getFromDatabase();
                // -------------- pure business logic ends here -------------- 

                // commit transaction
                commit();

                // deal with various exceptions
            } catch (DatabaseDownException ex)
            {
                log("Database is down");
                // email administrator
                emailDBA("Database is down; you're working all weekend");
            } catch (NetworkDownException ex)
            {
                log("Network is down");
                // email administrator
                emailNetworkAdmin("Network is down; you're fired");
            } catch (Exception ex)
            {
                // rollback transaction
                rollback();
                System.out.println("Caught exception; retry attempt #" +
                    numAttempts);
                t = ex;
            } finally
            {
                // close database connection
                closeConnection();
            }
        } while (numAttempts <= maxRetries);

        if (retval == null)
        {
            throw t;
        }

        // save in cache
        cache(retval);

        // stop profiler
        stopStopWatch();

        // trace exit
        log("Exit BusyBody");

        return retval;
    }
}
Although I knew better, I didn't have the tools to factor out all the orthogonal concerns:
  1. tracing
  2. profiling
  3. caching
  4. retry strategy
  5. transaction isolation levels
  6. transactional wrapping
  7. dealing with specialized exceptions
  8. cleaning up connections and other resources
All of these things can be regarded as "aspects" - cross-cutting concerns that ideally are managed somewhere besides your business logic, even more ideally in a reusable manner such that e.g.  NetworkDownExceptions are always handled the same way, and the response can be managed with single point of change; or transaction isolation levels can be managed from a central place; and etcetera. Factoring out these aspects buys you a maintainable codebase and central management of orthogonal concerns - and this will save long-term costs. Your manager should care deeply about this - since it has been said that maintenance is 80% of the overall software lifecycle cost. And since it will facilitate getting the busy code snippet above to look more like this:
public class CleanAndElegantService
{
    public Object fetch() throws Exception
    {
        return getFromDatabase();
    }
}

...I'm thinking you might care about this also (I'm assuming there's no need to elaborate on this - the difference should speak for itself). In subsequent posts I'll explore Spring 3.x AOP to see if we can't factor out all the aspects and move from busy-body to clean-and-elegant.

No comments:

Post a Comment