April 13, 2011
Wrote earlier about the importance of refactoring and continuous alignment within the context of systematic reuse effectiveness. Reducing technical debt is an integral aspect of refactoring. This post provides tips for reducing technical debt in your software assets:
- Minimize redundant definitions of key domain objects (i.e. competing, conflicting definitions of the same domain object across related applications)
- Minimize similar solutions for slightly varying business processes and instead create common process flows
- Loosen tightly coupled integration/transport logic with business logic
- Provide consistent strategies for implementing cross cutting concerns
- Replace tactical implementation for a problem that has a better open-source alternative
- Eliminate redundant approaches for managing configuration information
- Harmonize multiple, incompatible interfaces and make them more domain relevant
- Minimize excessive coupling with a particular technology/implementation stack
- last but not the least – create a comprehensive suite of automated tests
Are there similar themes in your development efforts? What steps are you taking to ensure technical debt is addressed?
April 25, 2010
One of the often-repeated reasons for not starting a reuse program is the upfront investment required for building the initial set of reusable assets. This is a legitimate impediment that can be addressed by taking advantage of existing code in your team. When you evaluate existing assets for their reuse potential, there are a lot of factors to consider. Here are a few salient ones:
- Fit within product line: is the asset of relevance to one or more applications in your domain? You want to invest in assets that encapsulate business functionality that can be utilized in multiple business processes and applications. Similarly, if there are assets with business relevance but in a area that your organization intends to divest out of – you will be less inclined to use them.
- Coupling: How tightly or loosely coupled are the pieces within an asset? sometimes the asset itself might be mixed in – tightly coupled – with a lot of extraneous code. Business logic might be mixed with data access, presentation, and even specific calls to external systems. Every one of these aspects introduces coupling that will need to be assessed thoroughly.
- Integration: what is the effort required to integrate the reusable asset with applications? The asset might be very difficult to integrate because of platform specific requirements or because of language needs.
- Business Assumptions: are there assumptions that the asset makes about business rules or business activities? if so, are these assumptions still relevant? will the rules need externalization/refactoring? Often, business assumptions are lost due to lack of alignment between business concepts and abstractions in code.
In a followup post, I will elaborate on robustness, scalability, deployment, documentation & samples. Without thinking through these aspects it is risky to introduce new assets for reuse. You will quickly discover that your consumers are unforgiving when it comes to asset quality. Additionally, a huge learning curve will drive away potential and existing developers and technical leads from evaluating assets for use with new applications.
April 24, 2010
Interfaces are fundamental to good design and more important when designing resuable assets. They provide several benefits:
- They model key behaviors that need to be supported – by designing interfaces, you are forced to partition a big chunk of functionality into a set of behaviors. The amount of detail in each interface varies but the very act to thinking through behaviors will provide a more loosely coupled design.
- Provides flexibility to change parts of a module/sub-system at anytime. This is specially relevant with projects where knowledge accumulates over time and domain understanding is unclear in during project inception. Interfaces help switch to a better implementation without adversely impacting the rest of the codebase (assuming, the contract stayed the same or only minimal changes were required).
- Explicitly support variability in an asset based on a defined set of behaviors. Interfaces are useful to encapsulate variations in a particular step (using Strategy), how different behaviors are combined (using pipe/filter) , or several steps in an overall algorithm (template method).
- With reusable assets, when you provide interfaces as contracts to your consumers – it gives you the freedom to change implementations and not break them. Note: this can be comforting but unless you ensure your consumers don’t instantiate concrete classes and don’t bypass integration-facing contracts there is no guarantee that they won’t break.
- Interfaces can potentially out live the implementation. This is related to #2 – as you learn new technologies, new ways to solving problems, interfaces give you the freedom to swap to a better implementation at any time. The interfaces that model domain concepts tend to be more stable as well – core business concepts don’t change overnight. However, business practices do change more frequently and the flexibility to combine and recombine concepts – conveniently captured using interfaces – is very powerful.
In a recently concluded project, I realized these benefits first-hand. The initial implementation of a search service was to go against the database directly. After a couple of iterations, we realized the need for a more effective and efficient solution – an indexed search engine. Even though the search module was used by several consumers the switch to a search engine based implementation was faster, more scalable, and most importantly, didn’t force consumers to make code changes.
November 21, 2009
A consuming application/business process wants to integrate with your reusable asset. However, the consumer doesn’t want to take your standard message – wanting a specific format that will make it easier for them. Why? Could be because of several reasons: lack of time/money/skills or technical limitations (e.g. their system can handle only delimited files and cannot parse XML).
The immediate, tactical (and often tempting) solution would be to just format the data per the consumer’s format and integrate in a point-to-point fashion. Don’t settle for this option too quickly! Prefer to publish a standard publication in that is in line with your long-term direction (e.g. offer reusable message publications in XML format indicating changes to critical data entities or state changes in business processes). Create a subscriber that tranforms the standard message to the consumer-specific format.
If point to point integrations go ungoverned, you will end up with a rat’s nest of tightly coupled integrations that ultimately hurt business agility.Your long-term intent is to have multiple consumers (web applications, backend systems, business processes, etc.) consume reusable message publications.If several business processes need the same core data or notifications about state changes, why would you want to integrate with them separately? Publications will not only reduce integration effort, they place less strain on your system resources – publish once and subscribe several times. No need to query databases or invoke external systems every time there is a new integration.
Note: The additional transformation logic will require extra logging/error handling but over the long haul is a better option than going for a point-to-point integration approach.
This will enable the asset provider, to integrate new applications faster (no need to custom develop messages and integration code for every new consumer) and reduce the cost of maintenance. Reducing application to application coupling is a key motivation for pursuing this approach as well.
Like this post? Subscribe to RSS feed or get blog updates via email.
November 6, 2009
In an earlier post, I elaborated on agile principles that facilitate reuse. As a follow up, I want to emphasize how principles of service orientation do the same from a complimentary perspective.
||How is this supported?
||Why is this supported?
||Service logic is built independent of specific product, technology, channel, and transport. Legacy services are wrapped to avoid direct coupling between providers and consumers. Both techniques are performed iteratively using refactoring. Decouple horizontal concerns such as logging, error handling, metrics, security from core logic.
||Lower the coupling, higher the opportunity for service reuse. Promotes agility by making less assumptions about upstream and downstream dependencies. Gives flexibility to service provider for performing continuous refactorings. Reuse horizontal capabilities for non-service components in the codebase.
||Code reviews and refactoring used to simplify and consolidate service contracts that aren’t well abstracted. Applies to both legacy and new services in the domain inventory. Abstract common service needs using multiple iterations and user stories.
||Abstraction clarifies domain intent and reduces needless coupling between providers and consumers. It eliminates point to point coupling between legacy services and consumers facilitating graceful migration.
||Refactor both new code and legacy modules within iterations. Identify opportunities for compositions when brainstorming implementation options for user stories. Use design patterns such as template method to design orchestrations.
||Composability is key for high levels of service reuse for implementing task services.
||Code and design reviews will be used to ensure reuse of data types, error codes, schema definitions, and that the contracts don’t expose implementation/technology specific details. If contract changes late in an iteration, add needed refactorings to iteration backlog and fix in a future iteration. A service is not “done,done” unless the contract is reviewed.
||Helps align contracts with logical data models for enterprise data entities. Delivers consistent behavior when a service is reused across physical transports. Helps create loose coupling among legacy interfaces and consumers.
||By decoupling legacy modules from being tightly bound increasing autonomy. Host services in a standard runtime environment.
||Autonomous services are easier to reuse both as standalone entities as well as in service compositions.
||Reuse strategy used to identify stateless services. Code reviews will identify and remove state that inhibits reuse.
||Stateless services offer higher degree of reuse and are simpler to scale to accommodate increased traffic.
||Building reusable services that can be mapped to user stories. Using legacy services to implement user stories and code reviews to remove duplication across the service inventory. Iteration planning allocates time for wrapping legacy services, unit testing, and working on refactoring backlog. Release planning is performed to ensure services are built within the context of an overall reuse strategy and a real consumer. Use product line techniques such as to allow service variations to accommodate varying consumer functional and quality of service characteristics.
||Minimize service development without a real consumer. Facilitates iterative and incremental manner for building reusable services. As user stories become better understood and code reviews have been done future iterations are continuously used to refactor the service.
Do these resonate with how you approach SOA and service orientation while building reusable service capabilities?
Like this post? Subscribe to RSS feed or get blog updates via email.
August 15, 2009
Reusable capabilities are available in legacy systems but first you have to loosen them up.