The beginning

Since the beginning of time, our corporation had been making proposals by hand, wasting precious time and effort. To make our life easier, we decided to take matters into our own hands, or to be exact out of it, create a web application, which is capable of creating proposal documents with only the proposal data required.

After having the idea, our first task was to create the workflow. Because of the Word document generation, we had to use .NET to integrate the Microsoft.Office.Interop.Word library. This was our first major .NET project, so we had to start the whole thing by researching and learning the best practices. After some trial and errors, we created an ASP .NET Core 3.1 Web API, that could handle proposal related data.

Because of the internal nature of the project, we could experiment a bit more than usual, so many times we chose the harder way to widen our know-how. This was basically a learning project, in the beginning, to learn all the tricks and tips about ASP .NET Core and its related technologies.

The basic idea was to create a 4-layered architecture: controllers, assemblers, services, and repositories. Controllers are basically the API endpoints, that call the services to handle the incoming requests. Assemblers convert our DTOs into database entities and vice versa. We replaced this layer later with AutoMapper because as it turned out if it is configured properly, there is not much difference in terms of speed. Services contain the main logic of the application. They are responsible for processing the incoming data and send it to the repositories to persist them. The repositories wrap the Entity Framework Core calls and logic for the services, to separate the responsibilities effectively. We used PostgreSQL to store our precious data.

We wanted a global exception handling from the beginning because, with proper error handling, the development is much easier. We decided to use an error-handling middleware which is called before every controller calls, and if an exception is thrown in the process, the middleware will catch and process it the way we want.

After a working foundation, we implemented the functionality to generate Word documents. For this we created two services: one for general Word generation in general e.g. inserting paragraphs or pictures, and another one for proposal generation, that takes the data required for the document and by the help of the general Word service, it creates our proposal Word document. On saving the proposal data, a new Word file is created and saved into the file system.

The Split

At this point, the backend service was too crowded. Everything was about managing the entities and files, it was all over the place and there was the Word generation, lying around. It was not right. So we got the idea: why not put the Word generation in a separate service? It only needs a Proposal to make the document anyway.

Then the day of separation came, planning a new microarchitecture for the standalone Word Generator service. It contains 3 projects, a web API, a Core generator, and a Proposal generator. The Core project contains all the interfaces and implementations to work with Microsoft Word. This is used in the Proposal generator, which knows, how to make a Word document for a Proposal. The web API is there to get some images from the original backend service, and so the created documents can be downloaded through REST calls.

Alright, but where is the communication about the Word generation? Well, that goes through RabbitMQ. We configured 2 queues between the original backend and the Word generator web API, to handle the multiple Word generation requests. One queue for sending the Proposal data, one queue for signaling the outcome of the generation process. Then, when everything is done and ready, the original backend service can download the generated Word file.

Architecture

The Future

In the future we would like to expand the Word generator microservice into a whole framework to be able to create multiple kinds of documents. With different topics, different compositions should be applied. Ideally to achieve this, we just need to change only one part of the whole architecture, like using plugins.

Another topic would be to have multiple templates available for one topic. Having different designs for generating a Proposal, for example, could be a nice addition to have a more personalized design.

Furthermore, there could be no application in these modern days without containerization. We are planning to introduce Docker in this project, but this requires some exploration. Word generation requires Microsoft Office Word to be installed on the host machine, which could cause some issues. Regarding the other services, it is clear what we need to do.