SuperOffice delivers multiple products. Customers see the Sales and Service applications, Customer’s customers see things like Chat, Forms, and the customer-facing side of Service; and both Customers and Partners see our programming interfaces.
Those interfaces exist in several different ways. At the least-technical end we have configuration and preferences – these are “interfaces” that require no programming, but can still influence what the application does to a considerable degree.
Then we have CRMScript and Configurable Screens, where a combination of programming and configuration is used to change the application.
If we step into the more technical programming domain, we come to the Web Service API of NetServer, consisting of the endpoints that are strictly defined in our service model. Building NetServer web services based on service-oriented/service-agent architecture allows us to overlay (or expose) our API in various standardized ways, such as SOAP, REST, OData, and potentially other protocols based on industry trends. These API’s are mostly accessed from programs, requiring a developer to write the code that uses them to perform some task.
Up to this point, Online and Onsite installations offer the same API functionality; and they do so in a way that hides the implementation details (as well as many database details) from the caller.
On a more technical level, we distribute SDK packages with the actual NetServer code, such that any public class XYZ is available to call. This gives access to lower-level code, including the ability to perform queries and updates on any table; access business logic. We also provide the ability to add logic with plugins that extend or override our functionality. It’s also possible to load a script file or DLL that our service API layer calls before and after any service API request is executed.
Parallel to that, it’s possible to supply XML files that extend or override the logic in our PageBuilder user-interface engine; typically, in connection with backend plugins that provide server-side logic.
These ways of accessing our code are only available Onsite, and it’s these that are the subject of the rest of this article.
The world is changing – in fact it never stopped
NetServer was designed quite some time ago, and I’m proud of the job the whole team did back then. While the world has progressed from .NET 1.0 up to today, the basics of NetServer have stayed the same – so much that we are almost completely compatible with code written 10 years ago. This has benefitted both us and the partners, providing a stable platform for development.
But while we have held still, the world has moved. Some fashions have come and gone, some have persisted, and new ones have arrived. Now the distance between the world we designed for and the world of today has become so large that we must move with it.
I am of course referring to .NET 6, but not only that, or at least not just the direct consequences. When Microsoft started the .NET Core project, one of their really fundamental decisions was to break compatibility with the platform. This means a valid .NET 4.8 program may not compile or run in .NET 6; and it may force a non-trivial rewrite. Examples of this include:
- When we first implemented NetServer, Windows Communication Foundation was brand new (SuperOffice was among the companies that sent a delegation to Redmond to beta test it). In .NET 6 it does not exist.
- NetServer was designed with “ambient variables” that keep track of identity, database, configuration and threading – as was the best practice at the time. .NET 6 is built around dependency injection and thus explicit management of these things.
- NetServer has a thread manager that is very conscious of when threads are created, run and destroyed; and integrates this tightly with the ambient variables. .NET 6 is totally oriented to async/await where actual threads are incidental and it’s in practice impossible to replicate the current NetServer approach.
We are now at the point where Microsoft has effectively put .NET 4.8 on life support. The latest versions of the C# language are not supported (see link). The tool chains are dying, and if you complain the answer is more and more often “hey, why are you still on 4.8?”.
Finally, the demands and idioms of modern testing are not compatible with the ambient variables and “implicitness” of NetServer. It was a good pattern for its time, but that time is behind us.
‘nuff said – we have known for some time that we need to move, and we have started to do so.
Compatibility – where and when
The most important point I wish to make here is this: Our API endpoints – the Agents and their methods – will stay the same. If you are already integrating with us by calling them via REST, you’re fine. Read the rest if you want, or just go do something else.
If you are calling us via WCF, please prepare to stop doing that and switch to REST. We will help, and we will keep WCF for a while yet, but just like .NET 4.8, WCF should be considered a dead end now.
If you have NetServer in-process and are calling or extending our code directly, it’s going to be worse.
It’s clear that we need to make some drastic refactoring in our code. Switching from ambient variables to explicit dependency injection is going to touch just about every class (even if much of the actual object management is done through factories, service providers and dependency resolvers at runtime).
The next onsite release of NetServer will contain the first block of these changes. Startup of NetServer will no longer be implicit/lazy, but will instead use the standard startup and composition patterns from .NET 6. We have made startup methods for various environments (WinForms/desktop; IIS web; win service; …) but it doesn’t happen by itself – in fact that’s the point of having an explicit startup.
It means that all integrations that run NetServer in process will have to add Startup code, suitable for their environment.
But it doesn’t stop there. It doesn’t make sense for us to just make the least possible amount of changes, because that would just incur the costs without reaping the gains. There are many things that can be done once we’re on .NET 6 – think about async/await; operation cancellation; better state and identity handling. All of these will need changes to public classes: for instance, to enable cancellation all the way to the database, cancellation tokens will make an appearance and that will be reflected in various internal api’s.
On top of that, there are functional refactoring's that we wish to make. There’s stuff in the userpreference table that should be elsewhere; the visiblefor table can be scrapped; user-defined fields and extra_fields should become the same thing, i.e. custom fields. There's 100.000 lines of generated code for Row field-change events that are never used. I could go on but the idea should be clear:
None of these changes will break the Service API; all of them will break some class compatibility and many will break low-level database compatibility.
A dark future?
As always, SuperOffice strives to satisfy all parties – customers, partners and ourselves. We actually believe in our own slogan, that relations matter; and we’re not going to make life difficult for anyone on purpose. However, we both want and need to move with the rest of the industry; we can’t let our ecosystem become obsolete in the name of compatibility. That would ultimately hurt everyone.
We therefore need to manage this transition as well as we possibly can. Our customers are migrating to the Cloud and in a sense that solves it, because the low-level integrations that get broken by our changes don’t exist there. We take the compatibility on the API level extremely seriously and do not plan to break it.
As far as possible, we will try to give reasonable warning times when we see that a breaking change is coming; and we will do our best to have help and advice available on how to tackle it. Any such changes break our own code too, so we definitely eat our own dog food and should therefore be able to present solutions.
We’d also like to involve partners to better understand what kind of changes hurt and how much; and if there are particular things we can do, avoid or retime to lessen the pain.
All that said… we’ve spent more than a year on preparatory work. Migrating our code management to the cloud; rebuilding tool chains; cleaning out all sorts of old spiders and bones from our code; investigating different technologies and alternatives. Now we’re at the point where the actual transitional work has started, and it will accelerate throughout this year.
While the transition may be a trifle rough, I very much look forward to when we are done. In tandem with the evolution of our Online platform, our code will be better positioned than ever to work in a Cloud environment, carrying developers, partners and customers from one era of computing to another. It's definitely not going to be boring.
Dr. Marek Vokáč
Chief Engineer, SuperOffice R&D