Introduction
The Beef framework, and the underlying code generation, has been primarily created to support the industralisation of API development.
A means to have software developers focus directly on the accelerated delivery of business value; with consistently higher quality outcomes at an overall lower cost.
The key industralisation goals are:
- Value - focus on business value, not on boilerplate
- Acceleration – improve velocity; reduce costs and time to market
- Simplicity – increase effective usage and minimise learning
- Standardised – increase knowledgeable resource pool
- Consistency – improve overall quality and maintainability
- Flexibility – enable innovation and evolution easily over time
Architecture
Beef has been developed to encourage the standardisation and industrialisation of the tiering and layering within the microservices (APIs) of an Application Architecture.
API-enabled domain-based channel-agnostic architecture
The conceptual architecture is as follows; with Beef being targeted specifically at implementation of the API tier.
The key concepts are as follows:
- Channel-agnostic - the APIs are based around the key entities and the operations that can be performed on them:
- APIs represent the key trust boundary; as such, they make no assumptions on the consumer. The APIs will always validate the request data, and house the application’s functional business and orchestration rules.
- APIs should not be developed to service a specific user interface interaction; as the APIs are agnostic to the consumer. The consumer has the responsibility of coordinating across API calls.
- Domain-based – the APIs are based around, and encapsulate, the capabilities for a functional domain:
- Outcome of a Domain-Driven Design; divides capapabilities into different Bounded Contexts.
- Encourages micro vs monolithic services.
Microservices
An architectural pattern for creating domain-based APIs:
- Is a software architecture style in which complex applications are composed of small, independent processes communicating with each other using language-agnostic APIs.
- These services are small, highly decoupled and focus on doing a small task, facilitating a modular approach to system-building.
- Implementation independence:
- Loose coupling – should have its own persistence repository; data is duplicated (synchronised), not shared; eventual consistency; no distributed transactions.
- Polyglot persistence / programming – use the best persistence repository to support the storage requirements; use a mix of programming languages (fit-for-purpose). Note: Beef provides a C# / .NET implementation approach as one option.
- Eventual consistency - for the most part, eventual consistency is good enough; real-time distributed transactional integrity is rarely required (although generally desired). An asynchronous messaging system, such as Queues or a Service Bus, can be leveraged to orchestrate cross domain data (eventual) consistency.
“Micro” doesn’t imply number of lines of code; but a bounded concept / business capability within your Domain. - http://herdingcode.com
Tiering and layering
The architecture supports a domain-based channel-agnostic microservices approach. The API service endpoints represent a light-weight facade for the Business (domain logic) tier, that is ultimately responsible for the fulfillment of the request.
The following represents the prescribed tiering and layering of the architecture:
Given this architecture, the .NET Solution you create using Beef should adhere to the prescribed solution structure.
Each of the key layers / components above are further detailed (Xxx
denotes the entity name):
- Entity (DTO) -
Xxx
- Service agent -
XxxAgent
andXxxServiceAgent
- Service interface -
XxxController
- Domain logic -
XxxManager
- Service orchestration -
XxxDataSvc
- Data access -
XxxData
Event-driven
To support the goals of an Event-driven Architecture Beef enables the key capabilities; the publishing and subscribing of events (messages) to and from an event-stream (or equivalent).
-
Publish - the publishing of events is integrated into the API processing pipeline; this is enabled within the Service orchestration layer to ensure consistency of approach. Beef is largely agnostic to the underlying event/messaging infrastructure (event-stream) and must be implemented by the developer.
-
Subscribe - a event subscriber is then implemented to listen to events from the underlying event/messaging infrastructure (event-stream) and perform the related action. The event subscriber is encouraged to re-use the underlying logic by hosting the Beef capabilities to implement. The Domain logic layer can be re-leveraged to perform the underlying business logic on the receipt of an event (within the context of a subscribing domain).
The Beef support for an event-driven architecture is enabled by the Beef.Events
assembly.
Framework
A comprehensive framework has been created to support the defined architecture, to encapsulate and standardise capabilities, to achieve the desired code-generation outcomes and improve the overall developer experience.
Standardised approach, ensures consistency of implementation:
- Reduction in development effort.
- Higher quality of output; reduced defects.
- Greater confidence in adherence to architectural vision; minimised deviation.
- Generation and alike enables the solution to evolve more quickly and effectively over time.
A key accelerator for Beef is achieved using a flexible code generation approach.
An extensive framework of capabilities has also been developed to support this entity-based development. Specifically around entities and their collections, entity mapping, reference data, validation, standardised exceptions, standardised messaging, basic caching, logging, flat-file reader/writer, RESTful API support, ADO.NET database access, Entity Framework (EF) data access, OData access, Azure Service Bus, long running (execution and triggers) processes, etc.
The key capabilities for Beef are enabled by the following run-time assemblies:
Assembly | Description | NuGet | Changes |
---|---|---|---|
Beef.Core |
Core foundational framework. | Log | |
Beef.AspNetCore.WebApi |
ASP.NET Core Web API framework. | Log | |
Beef.Data.Database |
ADO.NET database framework. | Log | |
Beef.Data.EntityFrameworkCore |
Entity Framework (EF) Core framework. | Log | |
Beef.Data.Cosmos |
Cosmos DB execution framework. | Log | |
Beef.Data.OData |
OData execution framework. | Log | |
Beef.Events |
Supporting Event-driven framework. | Log |
The tooling / supporting capabilities for Beef are enabled by the following assemblies:
Assembly | Description | NuGet | Changes |
---|---|---|---|
Beef.CodeGen.Core |
Code generation console tool. | Log | |
Beef.Database.Core |
Database and data management console tool. | Log | |
Beef.Test.NUnit |
Unit and intra-domain integration testing framework. | Log | |
Beef.Template.Solution |
Solution and projects template. | Log |
The following samples are provided to guide usage:
Sample | Description |
---|---|
Cdr.Banking |
A sample as an end-to-end solution to demonstrate Beef being used to solve a real-world scenario. This demonstrates an implementation of the CDR Banking APIs leveraging a Cosmos DB data source. |
Demo |
A sample as an end-to-end solution to demonstrate the tiering & layering, code-generation, database management and automated intra-domain integration testing. This is primarily used to further test the key end-to-end capabilities enabled by Beef. |
Additional documentation
The following are references to additional documentation (these are all accessible via links within this and other documentation):
Major versions
General
Solution
- Solution structure
- Entity (DTO)
- Service agent
- Service interface
- Domain logic
- Service orchestration
- Data access
Code-generation
- Code generation
- Template structure
- Entity-driven:
- Table-driven (database):
License
Beef is open source under the MIT license and is free for commercial use.
Getting started
To start using Beef you do not need to clone the repo; you just need to create a solution with the underlying projects using the prescribed solution structure, including referencing the appropriate NuGet packages. To accelerate this a .NET Core template capability is provided to enable you to get up and running in minutes.
See the following for example end-to-end usage; each demonstrating the same API functionality leveraging different data sources to accomplish:
Contributing
One of the easiest ways to contribute is to participate in discussions on GitHub issues. You can also contribute by submitting pull requests (PR) with code changes.
Coding guidelines
The most general guideline is that we use all the VS default settings in terms of code formatting; if it doubt, follow the coding convention of the existing code base.
- Use four spaces of indentation (no tabs).
- Use
_camelCase
for private fields. - Avoid
this.
unless absolutely necessary. - Always specify member visibility, even if it's the default (i.e.
private string _foo;
notstring _foo;
). - Open-braces (
{
) go on a new line (anif
with single-line statement does not need braces). - Use any language features available to you (expression-bodied members, throw expressions, tuples, etc.) as long as they make for readable, manageable code.
- All methods and properties must include the XML documentation comments. Private methods and properties only need to specifiy the summary as a minimum.
For further guidance see ASP.NET Core Engineering guidelines.
Tests
We use NUnit
for all unit testing.
- Tests need to be provided for every bug/feature that is completed.
- Tests only need to be present for issues that need to be verified by QA (for example, not tasks).
- If there is a scenario that is far too hard to test there does not need to be a test for it.
- "Too hard" is determined by the team as a whole.
We understand there is more work to be performed in generating a higher level of code coverage; this technical debt is on the backlog.
Code reviews and checkins
To help ensure that only the highest quality code makes its way into the project, please submit all your code changes to GitHub as PRs. This includes runtime code changes, unit test updates, and updates to the end-to-end demo.
For example, sending a PR for just an update to a unit test might seem like a waste of time but the unit tests are just as important as the product code and as such, reviewing changes to them is also just as important. This also helps create visibility for your changes so that others can observe what is going on.
The advantages are numerous: improving code quality, more visibility on changes and their potential impact, avoiding duplication of effort, and creating general awareness of progress being made in various areas.