This document outlines the design philosofy for GetIt's source code.
The source code for GetIt is made up of four layers
Each of these layers maps to a specific namespace in which contracts, implementations and factories are defined. Besides the presentation layer, each layer must only expose data types that are either defined in the standard C++ library or use data types defined in the domain layer of GetIt itself.
To communicate with classes from a different layer a factory must be used.
As a general rule, all interfaces are defined in the contracts interface. To imply the layer the
Namespace | Interface | Description |
---|---|---|
domain::contracts |
RequestFactory |
To create a new Request, the request factory is used. But to inject the factory itself, an interface is defined which used by the dependent classes. |
service::contracts |
RequestService |
To send a request, the request service is used to send the request object. But to inject the factory that instantiates the service for sending the request, an interface is used by the dependent classes. |
Namespace | Class | Description |
---|---|---|
domain::implementations |
RawRequest |
This class is used to represent a raw http request. This implies the use of a raw body. If the body is a string, that string is directly sent to the server without any parsing beforehand. * This class inherits from the Request model. |
domain::implementations |
FormdataRequest |
This class is used to represent a formdata http request. This implies the use of the multipart/form-data header. Every element or file that's added to this request is converted to the expected formdata http body, see the IETF RFC 7578 (Adobe, 2015). *This class inherits from the Request model |
domain::factories |
RequestFactory |
This class is used to instantiate a new Request model implementation based on the parameters. I.e. if the parameters are only applicable to a RawRequest , a RawRequest object is instantiated and returned. |
domain::models |
Request |
This is an abstract representation of an actual request. The getBody and getContentType methods are override by implementations so these are different for the different implementations of a request. |
domain::models |
Response |
This is a representation of a response returned by a server after sending a Request. |
service::implementations |
CppRestRequestService |
Implementation of the RequestService that's using the CppRestRequest library from Microsoft (Microsoft, n.d.). As of right now, this is the only implementation available for sending a request using the RequestService. |
service::factories |
RequestServiceFactory |
Factory that's used to instantiate a new RequestService. Because the CppRestRequest library from Microsoft is the only supported library for sending a request this class only constructs a CppRestRequestService object. However, in the future this factory could be expanded with other request libraries. |
In the following paragraphs each layer will be explained in more detail.
GetIt uses the layered pattern, or N-tier pattern, to decouple the different types of partitions most common applications consist of . This pattern groups classes by responsibility and encourages you to place them into the same layer (Price et Al., 2022). Thus meaning that all classes and logic related to data storage or retrieval are put into the data layer.
The table below lists and describes the different layers GetIt acknowledges.
Layer | Description |
---|---|
Domain | The domain layer consists of classes exclusive to the domain. This includes classes such as Request that are directly sent to a server. The domain layer can only contain classes that are dependent on other classes inside the domain layer. This is done to prevent knowledge of anything outside of the request domain. |
Service | The service layer is used to decouple the domain from the actual implementation of sending a request and retrieving a response from a server. The service layer can be thought of as the layer that can be used to interact with domain objects. Even though it's possible to interact with the domain objects, they can't be used to send an actual request. |
Data | The data layer is used to decouple the domain layer from storing domain objects. In the data layer it's possible to create multiple implementations of methods for saving a request, or retrieving a request. With this decoupling, the domain layer can be used to describe the domain without worrying about any details that are required for saving a request. |
Presentation | The presentation layer consists of QT-libraries and interaction for the GUI. The presentation layer is only allowed to communicate with the service and data layer. However, the objects from the domain layer are used as models in the MVC pattern. |
This paragraph describes the different decisions that were made during the design of GetIt.
GDD01 | Raw- and FormdataRequestBody |
---|---|
Decision | Create a different implementation for the RawRequestBody and FormdataRequestBody which implement from the RequestBody interface |
Motives | When working with a Request you don't need to know the specific body type, therefore an interface RequestBody is used |
Alternatives | Use different Request implementations for the different body types |
GDD02 | Abstract factory |
---|---|
Decision | Use the abstract factory pattern to inject factories into classes |
Motives | To make use of the IOC pattern, the factories are injected as interfaces as well. |
GDD03 | Inversion of Control (IOC) |
---|---|
Decision | Use inversion of control to communicate with dependencies |
Motives | To communicate with dependencies inversion of control is used to abstract the instantation of classes away from constructors/methods that are dependent on the implementations. |
GDD04 | Constructor injection |
---|---|
Decision | Dependency injection is implemented with constructor injection |
Motives | By using constructor injection the dependencies need to be built in the main.cpp file instead of relaying on the complexity of a dependency injection framework. However, in the future a DI framework might be implemented without changing the underlying code of GetIt if the DI framework supports constructor injection. |
GDD05 | Construct classes using a factory |
---|---|
Decision | Only construct new objects using a factory |
Motives | To keep the construction of new classes in a single place, factories are used to construct new instances of objects. However, some exceptions are expected. But these exceptions only remain in the convert classes or the main method. If objects are instantiated outside of a factory, it must be explicitly expressed in a comment. |
GDD06 | Use C++ standard 2017 |
---|---|
Decision | Using the C++ 2017 standard for compiling GetIt |
Motives | When compiling CppRestSDK with C++ standard 2020 the compilation fails due to implicit capture of ‘this’ via ‘[=]’ is deprecated in C++20 in the pplxtasks.h header. |
GDD07 | String literals |
---|---|
Decision | Only use string literals when there are no line breaks in the string. |
Motives | To keep the code readable and strings on one line, the use of \r\n line breaks must be used instead of actual line breaks in the string. |
GDD08 | Separate pipeline to create Github release |
---|---|
Decision | Use a different pipeline to create a release based on the latest tag. |
Motives | To keep the pipelines for packaging GetIt for different platforms independent of each other, the release cannot be created in one of those pipelines. When the pipeline that creates the release fails, the other pipelines subsequently fail as well. Therefore a separate pipeline is the best alternative. |