Getting Started with C# Microservices¶
Creating your first microservice will automatically create your solution for you. Following the guide below will generate the following recommended folder structure:
/repo-root
|- ProjectName.uproject
|- ProjectName.sln
|- ProjectNameBeamable/
|-- ProjectNameBeamable.sln
|-- services/
|---- MicroserviceName/
|------ MicroserviceName.csproj
Unreal and Version Control Systems
Most Unreal .[vcs]ignore
file templates you can find online ignore all *.sln
files. Instead of doing that, make sure to only ignore in the root directory with this /*.sln
instead of *.sln
.
In Git, you can also ensure your Microservice .sln
is not ignored explicitly via !/ProjectNameBeamable/ProjectNameBeamable.sln
.
Create the Microservice Solution and Project¶
Make sure the editor is not running before starting this guide.
Open a terminal/shell at your project's root and run the following command to create a new project and add a new microservice.
The created Microservice would look something like the example one below here.
[Microservice("MicroserviceName")]
public partial class MicroserviceName : Microservice
{
[ClientCallable]
public int Add(int a, int b)
{
return a + b;
}
}
Now that you have a microservice, you can compile it via your IDE.
Afterwards, you can run the command below from your Game Project's root.
Linking a project informs our CLI where it should put generated C++ files for communicating with the Microservice from UE code.
To generate the project files, run:
# Run manually whenever you make schema changes to `Callable` method signatures (or types used in them)
dotnet beam project generate-client ProjectNameBeamable/services/MicroserviceName/bin/path/to/built/dll
Automatic Code Generation & Limitations
You can also add <GenerateClientCode>true</GenerateClientCode>
to the BeamableSettings
Property Group inside MicroserviceName.csproj
; this will run the command on every re-build of the service DLL.
The automatic code generation is in early experimental stages and might see breaking changes in the near-future. For this reason, it is currently disabled by default.
After the generating the client in your Unreal project, these are the next steps:
- Add the generated plugin (
ProjectNameMicroserviceClients
) to youruproject
file and enable it. - Add the
ProjectNameMicroserviceClients
modules to yourTarget.cs
files. - Add
ProjectNameMicroserviceClients.AddMicroserviceClients(this)
line to yourBuild.cs
files.
Now, you're ready to write code that invokes the microservice.
Here's what that would look like:
// From inside any GameInstanceSubsystem, Actor or Component...
// Get the Generated API subsystem
const auto MsApi = GEngine->GetEngineSubsystem<UBeamMicroserviceNameApi>();
// Create an instance of the generated Request object.
const auto Req = UMicroserviceNameAddRequest::Make(1, 2, GetTransientPackage(), {});
// Declare the handler for the addition
const auto Handler = FOnMicroserviceNameAddFullResponse::CreateLambda([this](FMicroserviceNameAddFullResponse Resp)
{
// If the request failed or we are retrying, we do nothing
if (Resp.State != RS_Success)
return;
// Otherwise, print the value
UE_LOG(LogTemp, Display, TEXT("Microservice Add: %d"), Resp.SuccessData->Value);
});
// Make the request (the user slot will usually be the UBeamCoreSettings::GetOwnerPlayerSlot())
FBeamRequestContext Ctx;
MsApi->CPP_Add(UserSlot, Req, Handler, Ctx, {}, this);
Take a look at the Microservices docs for more information about where to go from here.
Optional - Create a MicroStorage¶
MicroStorages are a Beamable feature that allows you, the Game-Maker, access to a MongoDB you can use to define your own custom persisted data formats to go along with your own custom microservice logic.
This is a useful architectural alternative when making complex features that do not map well to the default cases of Beamable's own data-storing Managed Services (Stats, Inventory and CloudStorage).
To create a MicroStorage
, run the command below.
beam project new storage StorageName --sln ProjectNameBeamable/ProjectNameBeamable.sln \
--link-to MicroserviceName
This will add a StorageName.csproj
project to the solution and reference that new project in the MicroserviceName.csproj
file, making the code in the StorageName
project accessible to the Microservice.
Storages cannot exist independent of Microservices and must be associated with one at all times. The link-to
option will make the given Microservice depend on the newly created Storage. This dependency makes the storage accessible to the microservice.
Running Locally
MicroStorages require you to ALWAYS have docker running when running locally. Microservices that DO NOT DEPEND on storages can be run directly from the IDE without docker running at all. Microservices that DO DEPEND on storages can also be run from the IDE, but docker MUST be running too.
Take a look at this documentation for more information on how to write the code inside Storages and how to access them in Microservices.
Optional - Libraries and C# Microservices¶
One of the big advantages of Beamable C# Microservices is that they are regular .NET
projects. This means you get access to Nuget packages should you need to integrate Beamable with any particular Third-Party technology that we don't support out-of-the-box.
To do so, use Nuget to add a package dependency to a project (this can be done easily through any IDE --- or through dotnet nuget
command pallet).
While definitely not the common case, it is possible for projects to require multiple services. When that happens, it is also common that those services share some logic or internal data types. To write your own code that is then shared between services:
- Create a
dll
project by running this command:beam project new common-lib LibraryName --sln ProjectNameBeamable/ProjectNameBeamable.sln
- Adding a reference to it in any of your Microservice's
.csproj
files by adding this line to an ItemGroup block:<ProjectReference Include="..\LibraryName\LibraryName.csproj"/>
UE Code Generation Limitations
You can write any code here and share types between microservices. Keep in mind that, if you use these types in method signatures of multiple microservices, due to Unreal's lack of namespacing, you'll end up with two different UE types for the same shared type.
For that reason, we currently recommend you do not expose shared complex types inside Callable
method signatures.