Design a Customizing
Customizing UBIK® is a complex endeavor. In this article, we aim to provide a guide and best practices making this task as straight-forward as possible.
Strategy
It's important that development of your project is structured well in order to maximize the positive impact of every step you take. An unstructured project will lead to the frequent dismissal of invalid results, going back and forth unnecessarily, because things are done in the wrong order, ignoring the dependencies between tasks.
As a prerequisite for the rest of this article, please consider the following guide: Organize UBIK Development
The above article describes how to manage your project organizationally, but only implicitly covers how to deliver a solution to your problem. Assuming you have set up issue tracking, source code management and documentation, the following steps are recommended.
For the start of a project
- Collect the requirements from the customer
- Negotiate a functional specification with the customer, to define the scope of the project
- Design the top-level architecture
- Use the top-down approach to create a Project Structure Plan consisting of Epics, Features and User Stories
- Make an effort estimation based on the Project Structure Plan to negotiate a budget with the customer
For a topic throughout the project
- Collect the requirements for that topic from the customer or project leader
- Create a functional design
- Create a technical design
- For the choice of appropriate UBIK® tools and features, search the Wiki as described in the respective section
- Plan the architecture of your solution
- Design a data model
- Design the algorithm
- Perform the implementation
- Perform the QA including manual testing, automatic testing and reviews
- Update documentation
Make sure to maintain the Project Structure Plan for non-trivial tasks. In the rest of this article, best practices for the individual steps are described.
Architecture
The architecture is something to consider on a rather abstract level, in the beginning of a project, but also on the level of a single task. In the context of a whole project, architecture means defining whether there are multiple UBIK® installations on different sites, what products will be involved and how they are connected topologically and semantically. On task level, the same is interesting, just in greater detail.
Topology
In this context, topology addresses the technical architecture. Questions:
- What top-level modules do exist where?
- in the network
- in the customizing
- What interfaces do exist between them?
- using which protocol
- covering what data
Semantics
Semantics (the science of meaning) in this context addresses the functional architecture: which task-level modules are taking care of what responsibility, and how they play together. Questions:
- What is the purpose of a module?
- What information is exchanged via an interface, what does it do for the use-case?
Restrictions
We recommend the architecture to satisfy the following restrictions.
Top-Level architecture
- Network-distances between the database server and the application server (e.g., web services) should be minimal for performance reasons.
- One should always plan for a staging strategy with multiple environments for QA.
Task-level architecture
- Custom code should always be developed in a plugin where possible, with the UBIK® custom code just connecting the plugin code with the UBIK® objects.
- Custom code should be arranged so heavy-duty processes (involving much data and complex calculations) are performed on powerful machines (and not on the mobile client).
- Actually, all of the effort should be done on the server if possible, with the client just using out-of-the-box features. This won't be feasible in all cases, though.
Data Model
A good way to design a data model is to create an Entity-Relationship Diagram. It is recommended to do so in the scope of a Technical Design (see Requirement Supply Chain).
Object-oriented and relational
UBIK® is an object-oriented framework, so the data model should reflect entities of the customer's business processes (e.g., invoices and products), as so-called MetaClasses. One can use relations and references to create n:m and 1:n connections and dependencies between such entities (e.g., the relationships between invoices, invoice items and products). Properties with different data types can be modeled for every entity, and one such MetaClass can inherit the properties from another (e.g., there could be a MetaClass "SAFETY_INSTRUCTIONS" inheriting the "Filepath" property from a "DOCUMENT" MetaClass). An important aspect in data modelling, especially for the representation in relational databases, is normalization. See also:
Basic data model
For a use-case to be satisfied with UBIK®, we should create a basic data model for the entities involved in the relevant customer's business processes, without respect to presentation in UBIK® first. This is because we need to rely on a correct and well-defined concept of the data on a fundamental level.
In contrast, a common mistake is to start designing the data model according to the workflow one is trying to customize. There are the following problems with that:
- The data is not normalized correctly, because properties are not assigned to the "true" entity in the customer's process.
- As a consequence, there is redundancy in the data and the data model is inconsistent with reality, leading to conceptual problems when using the data later.
Extended data model
The basic data model might not be very good for representation on the mobile application, because it would lead to mixing multiple different entities in the same view. This is a problem because it makes the UI customizing complex and prone to performance issues.
But, worry not. UBIK® is designed to provide views and transformations of a basic data model. So, we can create an extension of the original data model in a second design phase, where we provide a model tailored to the (mobile) use-case. This extension can happen on multiple levels:
- A new layer of MetaClasses can be used to perform calculations on and combine more basic data.
- A View can provide a custom hierarchy between instances using relations, queries and custom implementations of view-connections between objects (so-called ViewItems).
- A Scope can define how to display objects on the mobile application, allowing for a custom representation in the UI.
Also, UBIK® comes with a lot of features one can apply to influence the behavior of (data on) the mobile client, ranging from inter-dependent tasks and work-packages to dynamic selective lists.
Algorithm
Most UBIK® projects have custom code performing more or less complex data processing tasks. The algorithm resulting from custom code can be highly distributed because of the way the life cycle of UBIK® objects can be customized. But it can also be a single interface with a third party system that has to crawl through a lot of data and perform many operations on them.
In any case, it pays off to get a good idea of the virtual processes resulting from the customizing for multiple reasons:
- Algorithms can scale very badly if their time complexity is not considered.
- Especially distributed algorithms can misbehave seriously and are very hard to maintain if they are not understood thoroughly.
These two reasons should be sufficient to invest a substantial amount of effort into making the algorithm:
- Easy to understand
- Scale well with respect to more input
There are also some quick hints we can drop for you to avoid many problems.
Custom code for MetaClasses
- Don't use the "OnSaved" event if you want to modify any object as a result of another object being saved. Use "OnPrepareForSave" instead, to avoid complex saving recursions.
- Use a custom plugin to create a nice architecture for your code, using the life cycle events only to call it.
Parallelism
- Avoid multi-threading, but if you have to implement parallel tasks, use locking wisely to avoid dead-locks.
- Keep in mind that multiple users could work on the same objects, i.e., your customizing can be executed in parallel, on different processes. This means you have to make sure your code doesn't interfere with itself. Mostly this is relevant for updating and reading from the database, where one process can update a value while another one is assuming a different value.
Performance
- Minimize the amount of relatively slow operations, like network operations, and filesystem or database access. Perform a few batch operations instead of many individual, small ones.
- Avoid nested loops, because they multiply the amount of processing steps required.
- Apply caching to reuse previously processed data and to avoid doing the same thing more than once, but clean up your cache if it becomes invalid.
Avoiding crashes
- Check every variable for being null before you use it.
- Think about the possible use-cases your code might encounter (e.g., extreme cases of parameters), and test these scenarios.
- For debugging, perform log outputs for unintended outcomes, providing runtime information you cannot inspect otherwise.
Well-formed code
- Apply the principles of object-oriented programming (OOP) and the C# coding conventions.
- For every responsibility in the code, there should be a dedicated software module. An optimal distribution of responsibility will often make your code perform better and easier to understand and maintain.
- Give all variables, methods and classes really good and human readable names. Good naming and well-structured source code is better than any kind of comments.
UBIK® Features
We are working on improving the Wiki documentation for UBIK® features, to provide a better understanding of what to achieve with which existing feature. One way to find the best feature for your task (and how to use it) is to prompt an AI chatbot like ChatGPT, which will search our Wiki for you. You could start with a prompt of the form: "Search the UBIK Wiki for information on <what you want>." Try to use different formulations if necessary. If the information cannot be found on the Wiki, please don't hesitate to open a support ticket on the Augmensys support portal.
Mobile
For mobile features, it is important to consider some basic principles the client works by.
Object hierarchy and navigation
- The navigation structure is based on the hierarchic relations between UBIK objects.
- One UBIK object is the primary context of every page, and a list of its children can be accessed from there.
- Showing multiple hierarchy levels of objects on the same page is most likely a performance killer and hard to maintain, too.
XAML customizing
- Every area of the application has a separate XAML file one can customize to adjust the layout.
- There is a central Themes XAML file for adjustments that can be reused in multiple areas.
- You can use the Developer Mode to find out which XAML file to adapt for a certain area in the UI.
See also:
Connectivity
- When designing a use-case, it is important to consider whether a feature must be available in offline mode, in online mode, or both; for offline use, the respective content must be made available on the device when there's the possibility (since it can't be fetched from the server on demand later).
Custom or Out-of-the-box?
- The usage of out-of-the-box features is highly recommended and preferred over the development of new custom features.
- New, custom features are problematic because of their need to be quality-assured, which is a lot of effort that was probably already invested for standard features.
- For the same reason, when customizing an existing feature always use the latest standard XAMLs as a starting point.