Many SOA initiatives leverage legacy capabilities when building new services. It is important to ensure legacy services are examined before leveraging them as-is. Here are some design techniques that I use when reviewing and refactoring legacy assets.
- Loosen the tight coupling among modules
- Understand the problem domain and work backwards
- Wrap legacy assets
- Keep backward compatibility in mind
- Remove point to point interactions with external systems
This post will elaborate on the first three and I will write a follow up post on the other two.
Loosen the tight coupling among modules
We've all worked with a legacy system where one module talks to another one which talks to another. In fact, a rat's nest of tight couplings might be the accepted way to address business requirements. Loosening the coupling between these legacy modules is the first step towards reusing any of the functionality. While you are doing this you will make the overall system simpler, easier to maintain, and pay down years (sometimes decades!) of technical debt. Refactoring these tight couplings will be very painful but it does get easier over time. Remember that you don't have to completely refactor all the tight couplings. If you what is needed for your immediate iteration that should be okay. The more you bite the more time and effort you need to invest in ensuring you didn't break the existing application. Also, you are touching a legacy component or process because you have an identified need that needs to be fulfilled.
To loosen the tightly bound legacy modules you could pursue several tactics:
* Identify and refactor logic that is tightly bound to user interface screens. The user interface is a good place to look for all kinds of logic being out of place.
* Identify business rules or policies that are embedded alongside data access or business logic code. You will want to refactor as much as you can to externalize them. Because every team is different you can place them in a class, a method, a database table, configuration file, or even a rules engine as appropriate (see James Taylor's post on getting started on decision management). It is more important to encapsulate than the exact means through which you implement. There is always the next iteration or release to change your mind if you were incorrect the first time.
* Pick batch jobs or scheduled file feeds that are associated with the legacy modules in question. Often you will find connectivity, file parsing/formatting, business logic all tightly bound in them. You will want to pick and choose what you want to go after but its an excellent place to start your refactorings
Depending on the complexity and nature of the couplings some of these might not be necessary for each legacy asset. By eliminating the tight coupling you will be positioned to reuse the asset for future projects. Slowly but surely, you will transform the legacy application and meet your release goals.
Understand the problem domain and work backwards
Your legacy systems often have domain relevant assets that have accumulated over time. It does help to sometimes look at the domain capability and work backwards to design and code changes. If a legacy asset isn't straightforward to refactor I find this technique very useful. Instead of mulling over what to change in the legacy codebase think about what it is that you really want. If you identify the domain relevant capability the problem becomes simpler to tackle and the changes needed start to appear naturally. For instance, your user story might be to offer a discount for a select set of users based on some business criteria. Maybe you have a legacy process that accesses customer data, transforms them, merges the data with other data loads, calculates discounts, and sends letters to your customers. It also has logic to load large files and records some metrics on them. Make a head-on refactoring of this process and you will have trouble where to start the changes (not to mention the fear of breaking working code!). Instead, step back and remind yourself what you really need. You just need the discount calculation logic and nothing else. Refactor just this logic and create a DiscountCalcualator reusable asset. The rest can stay as-is and you can always come back for more refactorings.
Wrap Functionality
Your enterprise probably has legacy software assets that are extremely valuable. It is also possible that you don't have the time or extensive knowledge to fully refactor a legacy asset and create a new one for use. In such cases you might need to make do with leaving the legacy asset where it is and still use it for your needs. If you start reusing them as-is though you will end up with lots of point to point connections between your code and the legacy asset. It will become tricky to change, upgrade, or get rid of the legacy asset. You could break consuming code and force changes every time you touch the legacy asset. Worse, you will expose your legacy error codes, naming conventions, behaviors across your codebase. Create a component that wraps the legacy asset which is between the legacy asset and the consuming code. If you don't have a wrapper that truly encapsulates the legacy functionality it will serve only as a pass-through layer adding no value. This wrapper component helps you reuse these legacy assets by enhancing the underlying functionality. To be effective, this wrapper needs to do several things:
* If you have a team coding conventions/standards make sure this wrapper follows them.
* You will want to reuse existing assets for cross cutting functionality such as logging, error handling, and security
* You will need to translate legacy error codes, error messages, and use standard error codes
* If you need internationalization of messages/labels this wrapper can perform necessary translations
* If the wrapper is a web service you will want to reuse data types and XML schema contracts to get alignment with your logical data model. If it is a library component it needs to reuse existing base classes as appropriate.
The wrapped asset now can be reused by multiple consumers and you have the flexibility to change the implementation. You get to save time and still not introduce tight coupling into your codebase. Ask your team to stop using legacy assets directly and always go through the wrapper. Make this a practice when you consume legacy assets.













Leave a comment