The Service/Proxy/Business Logic Crunchup

Posted on November 30, 2022 by Michael Keane Galloway

Early in my tenure on the LoopNet team, we decided that we needed to do a re-write of LoopNet. At the time, LoopNet was one large mudball with all of the business and presentation logic wrapped in one gigantic ASP.Net application. We were hoping to break apart the mudball in order to help streamline the integration of LoopNet with the CoStar database. We also wanted to create a more moduler system that could be reflected in our JavaScript to promote re-use on the front-end as well.

Ultimately we decided to have a three tiered architecture on the back-end with: Controllers, Services, and Proxies. We had both MVC Controllers and API Controllers. The MVC Controllers were to be split apart into modules. These were MVC Areas with their own routing, Views, Models, SASS files, and their own legacy Angular modules. Some of these module areas were supposed to be for re-usable front-end code and the Controllers were simply supposed to contain presentation logic for end-to-end testing of re-usable front-end code. API Controllers were all contained in a single directory called Service. The more presentation centric module Controllers and the API Controllers were to not have any business logic.

The Service classes were to encapsulate all of the business logic, and were to be more of a domain driven design. For example, you should expect to see an Order service for managing all of the business logic for entering orders with the new E-Commerce platform we built during this re-write. The Service layer would then rely on Proxies to handle all of the communication between LoopNet and new micro-services that we wrote. So the aforementioned OrderService would have an OrderProxy that would handle all of the HTTP calls from LoopNet to the E-Commerce micro-service.

Unfortunately, this architecture did not survive long into the project. One of the main places that developers compromised was business logic in the Controllers. There were many places where we had a simple service that just returned the result of calling the proxy leaving all of the business logic to the calling controller. We even had Controllers in the re-usable modules that were used to encapsulate business logic resulting in Controllers that would allocate new instances of other Controllers to call methods on the new Controller instance.

We ended up with thicker Proxy classes as well. Developers would combine business logic with the client logic for calling micro-services. Then because all of the business logic was in the Proxy class, the Service class would again just be a thin abstraction over the proxy adding nothing but an extra function call on the stack.

Another issue we ultimately had was poorly split domains for these services. LoopNet is a website that shows commercial real estate listings. We had a ListingService that contained logic for searching listings, getting listing details, managing ACLs for listings, all of the logic for listing leads that a broker might want to see, and on and on. I think at one point I factored out the leads logic from the ListingService and emailed all of the managers involved to point out that between 3 teams we had at least 5 different domains in the same Service, and we could probably break this apart.

And now we’re re-writing LoopNet again… This time around we are aiming to create micro front-ends which at the very least keeps us from doing another grand re-write. But it has me thinking about how we ended up not following through on the previous architecture. I think a lot of it came down to a lack of review (we had TFVC, so we didn’t have a practice of reviewing pull requests), a lack of continuing communication about what patterns are appropriate within our architecture, and a lack of leadership distributed through our teams.

On that final leadership note, we had a concentration of leadership material on one team. I think that didn’t help, because they weren’t exposed to enough of our developers to help edify, grow, and educate other developers. The current structure of the LoopNet team has a much better distribution of developers that can help support better practices amongst different teams. I hope we learn from the past, and that someone outside our team can learn from our mistakes as well.