CCLLC.Core.ProcessModel.

Objects in the CCLLC.CoreProcessModel define interfaces and provide some default implementations for creating processes that are independent of data access implementations and therefore more portable and extensible.

Processes built on this model generally have three distinct architectural layers:

Dependencies
Nuget Packages

Why Should I Do This?

For someone like me, whose career evolved around SQL Server, Microsoft CRM, and now CDS and now the Power Platform, the idea of separating your logic code from your entity structure might seem a bit silly. Until a few years ago I agreed with you.

What I have discovered as I started working on larger projects is that there are more unknowns in the beginning of the project than you think there are. Even when you absolutely know your going to use a certain technology, such as Model Driven PowerApps and CDS, there is still a lot of value in separating your business logic from you data storage and access. Here are some real world examples from my recent experience where making the separation in the beginning of the project would have paid dividends later:

One other, and perhaps, more important personal outcome of switching to this model has been that my solutions are easier to read, better thought out, and far more testable.

All that being said, it does not make sense to use this model for trivial processes, but there are many, many, non-trivial processes in the world.

How It Works

This module addresses six items that I routinely need in when coding a business process for either a CDS plugin, An Azure Function, an IIS API, or a Windows Service:

  1. An execution context that gives my process access to the tools and data it needs.
  2. A system agnostic way to point to a record.
  3. A system agnostic way to pass data access components through the process to the data layer that will use them.
  4. A cache to store items that don’t change often and therefore cut down on unneeded trips through the data layer.
  5. A settings provider that allows reconfiguration of parts of the process when desired.
  6. An easy way to capture tracing and track particular events to a logging system.

Process Execution Context

The process execution context is defined by the CCLLC.Core.IProcessExecutionContext interface. Code built on the this module assumes that any “execution request” from the calling process event handler (e.g. plugin or Azure Function) will pass in an execution context that implements this interface. The process will use the items provided in the execution context to complete the work. The IProcessExecutionContext provides getters or methods for the following:

  1. A handle to the data service that is passed from the process orchestration level, through the business logic level, to the data layer level. The orchestration and data layers share some knowledge of how data access works, but the business layer is ignorant and literally has now means to perform CRUD operations.
  2. Access to a cache that can store and retrieve typed objects.
  3. Access to a setting provider that provides a simple name-value-pair settings access
  4. Access to an [IoC/DI container] (CCLLC.Core.IoCContainer.md) for creating dependencies that were not directly injected into the process at object creation or execution.
  5. A mechanism to log trace messages to a logging system.
  6. A mechanism to track named events to a logging system.
  7. A mechanism to track exceptions to a logging system.

Data Access and Data Pointers

The CCLLC.ProcessModel.IDataService interface provides the handle for passing a data service or connection from the orchestration layer to the data layer. The data layer needs to know how to convert the underlying object into an interface that can actually interact with data.

The CCLLC.ProcessModel.IRecordPointer interface provides a system agnostic way to point to a records in a data table based system. The IRecordPointer provides getters for Record Type (e.g. entity name) and Id. This is analogous to the EntityReference provided by the Microsoft.Xrm.Sdk. However, IRecordPointer is not locked in to GUID based unique record identifiers. It can represent alternate pointer types such as int, or strings.

The CCLLC.ProcesModel.RecordPointer class provides a default implementation of the IRecordPointer interface.

Cache

The CCLLC.Core.ICache interface defines the cache implementation used in the process model and the CCLLC.Core.DefaultCache implements that interface using the System.Runtime MemoryCache.

Settings Provider

Access to name-value paired settings is provided through the CCLLC.Core.ISettingsProvider interface. CCLLC.Core.SettingsProvider provides the default implementation of that interface. The default settings provider is created using the CCLLC.Core.SettingsProviderFactory class.

The GetValue<T>(key, defaultValue) method supports type casting the string value into most common data types such as int, decimal, and Boolean. These type conversions are based on the Convert.ChangeType method. In addition to these standard conversions, the following specific conversions are supported:

The SettingsProviderFactory requires a data connector that implements the CCLLC.Core.SettingsProviderDataConnector interface. This data connector implementation must be registered in the IoC Container as in the orchestration layer of the overall process. The container will create the data connector and inject it into the factory which uses it when creating a loaded setting provider.

The factory caches the created settings provider object for 15 minutes. This default cache timeout can be altered by creating a setting in the setting provider named “CCLLC.SettingsCacheTimeOut” with a numeric value indicating the number of seconds to cache the settings.

Tracing and Tracking

IProcessExectionContext provides four method signatures for capturing logging information:

The actual implementation of these methods and therefore the outcome of the method calls is left to the implementing class.