Architecture and Data Flow
Estrelica.Core's architecture comprises three conceptual levels:
APIFacade class - This class sits at the base of all operations, and provides the integration with Archer's APIs and CastleHill's extensions. This class handles all of the session negotiation, serialization, data type translation, error handling and retry operations for all API calls.
Resolver classes - This set of classes wraps the APIFacade methods with caching behavior, identity resolution, and conversion of Archer's JSON and XML API results into strongly-typed .NET entity classes.
Entity objects - This set of classes represents the entities (metadata and content) present in your Archer instance, returned by the resolver classes. All entity objects expose related entities via properties, allowing you to code against your Archer metadata and content as a single easily-traversible .NET object graph.
These conceptual levels are described in detail below.
APIFacade
At the heart of Estrelica.Core is the APIFacade class, a thin wrapper around Archer's REST and web services (SOAP) APIs which makes it trivially easy to call these API methods. Additionally, if extensions are available, it fills in some of the gaps in Archer's APIs by introducing new API methods which follow the same calling conventions. The API facade also handles serialization tasks automatically, so that instead of working with strings of XML or JSON, your code can access the results instead as IDictionary<string, dynamic>. As part of its deserialization the APIFacade also converts Archer's integer type Ids into meaningful .NET enumerations, so that instead of wondering what
module["ModuleType"] == 2
means, you can instead evaluate it as
module["ModuleType"] == ModuleType.Questionnaire
The same applies to all of the other integer type Ids returned by Archer's APIs. Each type represented in Archer's results as a simple integer (e.g. FieldType, ContactType, DisplayControl, etc.) is converted on the fly into meaningful human-readable enumerations. This brings clarity to the code you write and helps avoid errors that arise when you accidentally type 231 instead of 23.
Resolvers
If you're comfortable working directly with Archer's APIs that way, and you know all the possible dictionary keys for all of the JSON objects that Archer's API returns, you can use the APIFacade directly to implement your applications. That still requires a lot of internal knowledge about Archer, however, and understanding of the interrelationships between different entity types in the Archer framework.
These are problems that the Estrelica.Core's resolver classes take care of for you. You don't have to know any dictionary keys, nor do you really even need to know things like how Modules are related to Event Rules, or how to get from one to the other via API calls. The resolvers implement all of that knowledge for you.
The way they do this is by wrapping the APIFacade methods and the results that they return in strongly-typed objects, interrelated via strongly-typed properties, with background intelligence to make API calls on demand as needed to access related entities.
For example, the APIFacade.GetFieldDefinitionsForLevel() method returns an IEnumerable<IDictionary<string,dynamic>> result set, representing all the field metadata for a given Archer level. You may choose to iterate this to find all the calculated fields of a particular type, such as Values List fields, by doing something like this:
foreach(var field in core.APIFacade.GetFieldDefinitionsForLevel(191))
{
if (field["IsCalculated"] == true && field["FieldType"] == FieldType.ValuesList)
{
// found a calculated Values List field
}
}
but that requires you to know the integer Id of the level you're interested in, as well as the key names for the "IsCalculated" and "FieldType" properties of the field (and to make sure you don't accidentally misspell them, since the compiler won't prevent you from doing this).
Instead, it's better to retrieve these as strongly-typed references via the Metadata resolver, so you can rely on Intellisense and the .NET compiler to help you find the correct properties:
foreach (var field in core.Metadata.LevelById(191).Fields
.Where<IValuesListField>(f => f.IsCalculated))
{
// found a calculated Values List field
}
In this example, Level Id 191 happens to reference the "Subsidiary" level of the "Third Party Profile" application in a standard out-of-the-box Archer installation. If you don't know the Id mappings for all of the entities in your Archer configuration, however, it's far easier to let the resolver look these up for you by using a better-known identifier like name or alias:
foreach (var field in core.Metadata.ApplicationByName("Third Party Profile")
.Level("Subsidiary").Fields.Where<IValuesListField>(f => f.IsCalculated))
{
// found a calculated Values List field in "Third Party Profile" -> "Subsidiary"
}
Entity Objects and model traversal
Related entities are easily discoverable via properties and methods on the instances returned by the resolvers. This is demonstrated in the example above, where the IArcherApplication reference returned by ApplicationByName("Third Party Profile") has a Level(string nameOrAlias) method which will return a specific IArcherLevel reference from that application, which in turn has an IEnumerable<IArcherField> property which returns all the fields for that level.
This involves three separate calls to three different APIFacade methods, but you'll notice that there are no API calls present in the code above. This is because the resolver classes handle this for you automatically. Futhermore, they do this on demand, caching the results for future reference, so that API traffic is optimized by ensuring the API methods are only invoked when their results are actually needed.
This makes it very easy to traverse the spiderweb of metadata and content that is the Archer framework object model. Each entity in the system exposes properties and methods which return any entities it might have relationships to, and these in turn are backed by appropriate API calls to hydrate those results as they are needed. So in this example, once you've accessed an IArcherApplication instance, it's very easy to work your way down through its IArcherLevels and then to its IArcherFields.
This works in reverse too. For example, if you've retrieved a single IArcherField in isolation from the metadata resolver via its Id, it's very simple to work your way up the hierarchy via properties as well:
var field = core.Metadata.FieldById(13991);
if (field != null)
{
bool isCalculated = (field as IIsCalculatedProperty)?.IsCalculated ?? false;
Console.WriteLine($@"Found {(isCalculated ? null : "non-")}calculated {field.FieldType} field '{field.Name}' in level '{field.Level.Name}' of {field.Level.Module.ModuleType} '{field.Level.Module.Name}'");
}
This demonstrates how a standalone IArcherField can return its IArcherLevel and IArcherModule via property traversal. In a standard Archer environment, this will output the string:
Found calculated ValuesList field 'Performance Rating For Relationship' in level 'Subsidiary' of Application 'Third Party Profile'
Data Flow
Estrelica.Core integrates with your Archer instance in three ways.
All three license levels (Basic, Professional, and Enterprise) connect directly to your Archer instance via the standard public Archer APIs (REST and webservices).
Additionally, the Professional and Enterprise licenses extend these APIs to expose additional entities and functionality which are not available via the standard APIs. Estrelica.Core does this by querying the Archer instance database. These extended calls may be made directly via SQL connection (if Estrelica.Core is configured with a valid connection string to your Archer instance database) or indirectly via HTTP REST API calls (if the CastleHill Extended API has been deployed to your Archer server).
Estrelica.Core Trial licenses permit the use of direct database calls in order to evaluate these extensions. The CastleHill Extended API is available with the Professional and Enterprise licenses.