DOTNET 버전 6에서 생성 된 구성 요소, 마이크로 서비스 및 기타 자동화 유틸리티 간의 사용자 정의 워크 플로우, 또는 질문을 위해 설계된 라이브러리
이 라이브러리는 모든 유형의 프로세스의 오케스트레이션을 지향하는 엔티티의 생태계를 갖도록 설계되었습니다. 여러 클래스 간의 통화와 같은 가장 간단한 프로세스에서 마이크로 서비스에 대한 통화 등과 같은보다 복잡한 프로세스에 이르기까지 항상 선언적 인 방식으로 "IF", "Else"등의 지루하고 복잡한 중첩 측면에서 코드를 저장하려고합니다.
이 저장소에서는이 생태계의 핵심 클래스가 될 것입니다. 이러한 클래스 중 일부는 독립적으로 유용 할 수있을 정도로 일반적이기 때문에 워크 플로를 만드는 범위 외부에서 사용할 수 있습니다.
이 네임 스페이스에서 우리는 Libray 클래스 내에서 사용되는 유틸리티 클래스를 찾았습니다.
개념 컨텍스트는 매우 일반적인 것이며 여러 분야에서 사용됩니다. 우리에게는 컨텍스트는 여러 구성 요소간에 값이 공유되도록 저장되는 공통 공간이 될 것입니다.
그러므로 우리는 값이 모든 유형의 키/값 시스템 앞에 있습니다.
컨텍스트의 구조는 간단하며 컨텍스트 클래스에 의해서만 형성되며, 값 클래스의 입력 및 검색 이 될 것입니다.
기본적으로 IconTextVault 를 구현하여 값을 메모리에 저장하지만 다른 방식으로 이러한 값을 저장하는 다른 구현에 대해서는 열려 있습니다.
우리는 클래스 컨텍스트 필드를 사용하여 컨텍스트에서 값을 얻고 설정하며,이 클래스를 사용하면 클래스의 유형과 이름을 정의 할 수 있습니다.
컨텍스트에서 가치를 얻는 방법의 예
var context = Context . Create ( ) ;
var field = ContextField < int > . Create ( "FieldName" ) ;
//Set Value
context . Store ( field , random . Next ( 1000 ) ) ;
//Get Value
var value = context . Value ( field ) ; 우리는 상태 머신의 정의 및 실행에 대한 여러 인터페이스를 처분했습니다. 기계의 정의는 커플 링과 책임의 분리를 피하기 위해 기계 자체의 실행과 분리됩니다.
메인 인터페이스는 이카신 입니다. 이 인터페이스를 사용하면 현재 상태, ISTATE 인터페이스 및 전환, ITRANSACTION 엔티티를 다른 상태로 생성하는 동작 코드를 사용하여 다른 상태로 전환 할 가능성이 있습니다.
전환을 통해서만 한 상태에서 다른 상태로 직접 갈 수는 없으므로 특히 우리가 한 상태에서 볼 수있는 모델이 있습니다.
정의 된 전환이없는 상태가 제공 될 수 있으며 이는 상태가 터미널임을 의미합니다. 이런 식으로, 우리는 유한 또는 비금속 기계를 정의 할 수 있습니다.
런타임 부분과 관련하여 기계의 정의는 ImachineEdeFinition 인터페이스에서 수행되며 MachineInedEfinitionBuilder 클래스에서 생성됩니다.
기계 정의 코드의 예
//Helper class with states enumeration
public class State : Enumeration
{
public static readonly State Init = new State ( 1 , nameof ( Init ) ) ;
public static readonly State Working = new State ( 2 , nameof ( Working ) ) ;
public static readonly State Paused = new State ( 3 , nameof ( Paused ) ) ;
public static readonly State Finished = new State ( 4 , nameof ( Finished ) ) ;
private State ( int id , string name ) : base ( id , name )
{
}
}
//Helper class with action enumerations
public class Action : Enumeration
{
public static readonly Action Start = new Action ( 1 , nameof ( Start ) ) ;
public static readonly Action Pause = new Action ( 2 , nameof ( Pause ) ) ;
public static readonly Action Continue = new Action ( 3 , nameof ( Continue ) ) ;
public static readonly Action Finish = new Action ( 4 , nameof ( Finish ) ) ;
private Action ( int id , string name ) : base ( id , name )
{
}
}
//Now we can create a definition
_definition = MachineDefinitionBuilder . Create ( )
. InitialState ( State . Init )
. OnAction ( Action . Start ) . ToState ( State . Working )
. Build ( )
. AddState ( State . Working )
. OnAction ( Action . Pause ) . ToState ( State . Paused )
. OnAction ( Action . Finish ) . ToState ( State . Finished )
. Build ( )
. AddState ( State . Paused )
. OnAction ( Action . Continue ) . ToState ( State . Working )
. Build ( )
. AddState ( State . Finished )
. Build ( )
. BuildDefinition ( ) ;기계 생성 및 사용 코드의 예.
var machine = Machine
. Create ( SimpleMachineDefinition . GetDefinition ( ) ) ;
machine . Dispatch ( Action . Start ) ;
var currentState = machine . State ; 이 네임 스페이스에서는 워크 플로를 정의하고 실행하는 데 필요한 클래스가 있습니다. 이전 섹션에서와 같이 워크 플로 정의를 실행과 별도로 유지합니다.
이 분리는 iWorkFlowDefinition 및 iWorkFlowRunner 인터페이스를 사용하여 수행됩니다.
정의와 실행을 캡슐화하기 위해 iflow 인터페이스가 있으면이 인터페이스는 미래에 서브 플로우를 구축하고보다 복잡한 응용 프로그램 내에서 서비스로 캡슐화 된 흐름을 만들 수 있습니다.
기본 흐름에서 필드 값을 재설정하기 위해 기본 흐름과 초기 노드를 생각하면 다음 노드는 랜덤 숫자로 caculation하고 두 값을 모두 합산하는 최종 노드는 정의가 그렇게해야합니다.
예제 워크 플로 정의 코드.
var contextDefinition = ContextDefinition . Create ( ) ;
var definition = FlowDefinitionBuilder . Create ( )
. WithInitialNode ( ResetValue . Create ( Node . Reset , contextDefinition ) )
. OnExitCode ( ResetValue . ExitCode . Ok ) . GoTo ( Node . SetValue )
. Build ( )
. WithNode ( SetValue . Create ( Node . SetValue , contextDefinition ) )
. OnExitCode ( SetValue . ExitCode . Assigned ) . GoTo ( Node . SumValue )
. Build ( )
. WithNode ( SumValue . Create ( Node . SumValue , contextDefinition ) ) . Build ( )
. BuildDefinition ( ) ;이전에는 ContextDefinition 과 같은 일부 도우미 클래스를 정의했습니다. 컨텍스트 필드를 포함하고 이름 정의와의 복제를 피하는 클래스 일뿐입니다.
internal class ContextDefinition
{
public ContextField < int > FirstDigit { get ; }
public ContextField < int > SecondDigit { get ; }
public ContextField < int > Result { get ; }
private ContextDefinition ( )
{
FirstDigit = ContextField < int > . Create ( "FieldOne" ) ;
SecondDigit = ContextField < int > . Create ( "FieldTwo" ) ;
Result = ContextField < int > . Create ( "FieldResult" ) ;
}
public static ContextDefinition Create ( )
{
return new ContextDefinition ( ) ;
}
}우리는 또한 모든 노드 클래스의 기본 클래스로 사용할 수있는 추상 클래스 를 만들었습니다.
internal abstract class Common : Core . WorkFlows . Implementations . Node
{
protected ContextDefinition ContextDefinition { get ; }
protected Common ( CommonNamedKey key , ContextDefinition contextDefinition ) : base ( key )
{
ContextDefinition = contextDefinition
?? throw new ArgumentNullException ( nameof ( contextDefinition ) ) ;
}
}inode 및 inodeasync 인터페이스 아래에는 두 가지 노드 유형 동기화 및 비동기가 있으므로 두 유형의 프로세스의 래퍼로 노드를 사용할 수 있습니다. 이 예에서는 동기화 구현 만 있습니다.
이 예에서는 동기화 노드 만 사용합니다.
예제 노드
internal class ResetValue : Common
{
#region ExitCodes
public class ExitCode : Enumeration
{
public static readonly ExitCode Ok = new ExitCode ( 1 , "Ok" ) ;
private ExitCode ( int id , string name ) : base ( id , name )
{
}
}
#endregion
private ResetValue ( CommonNamedKey key , ContextDefinition contextDefinition ) :
base ( key , contextDefinition )
{
}
public override NodeExit Execute ( Context context )
{
context . Store ( ContextDefinition . FirstDigit , 0 ) ;
context . Store ( ContextDefinition . SecondDigit , 0 ) ;
context . Store ( ContextDefinition . Result , 0 ) ;
return NodeExit . Create ( ExitCode . Ok . Name ) ;
}
public static ResetValue Create ( CommonNamedKey name , ContextDefinition contextDefinition )
{
return new ResetValue ( name , contextDefinition ) ;
}
}내부 클래스 Exitcodes는 또 다른 도우미 클래스 이며이 노드에 대한 avaliable exit 코드의 정의로 열거 클래스를 구축합니다. 우리는 Somthin Similira도 사용합니다.
러너는 스스로를 인스턴스화하기 위해 워크 플로 정의와 노드간에 정보를 공유하는 데 사용될 컨텍스트 인스턴스를 받아야합니다. 러너가 실행되면 컨텍스트에서 반환 값을 검색 할 수 있습니다.
우리는 추상 클래스 FlowRunnerBase를 가지고있어 커스텀 러너, 단계, 배포 등을 구현할 수 있습니다.
예제 흐름 러너
var flowRunner = FlowRunner . Create ( definition , Context . Create ( ) ) ;
var exit = await flowRunner . Start ( ) ;우리가 이전에 말했듯이 클래스 흐름은이 모든 프로세스의 래퍼이며 이제 기본 기능이 있지만 향후 버전에서는 워크 플로 관리를위한 메인 클래스로 사용됩니다.
var definition = SimpleFlowDefinition . GetDefinition ( ) ;
var context = Context . Create ( ) ;
var flow = Flow . Create ( FlowRunner . Create ( definition , context ) ) ;
var exit = await flow . Run ( ) ;