Compare commits

..

No commits in common. "main" and "v2.0.0" have entirely different histories.
main ... v2.0.0

545 changed files with 37331 additions and 18805 deletions

View File

@ -1,3 +0,0 @@
{
"appHostPath": "../src/Aspire/src/AppHost/AppHost.csproj"
}

View File

@ -3,28 +3,18 @@
"isRoot": true,
"tools": {
"dotnet-outdated-tool": {
"version": "4.6.9",
"version": "4.6.4",
"commands": [
"dotnet-outdated"
]
],
"rollForward": false
},
"dotnet-ef": {
"version": "10.0.3",
"version": "8.0.8",
"commands": [
"dotnet-ef"
]
},
"aspire.cli": {
"version": "13.1.1",
"commands": [
"aspire"
]
},
"csharpier": {
"version": "0.30.6",
"commands": [
"dotnet-csharpier"
]
],
"rollForward": false
}
}
}
}

View File

@ -9,167 +9,127 @@
## https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
## https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
## Microsoft Rules
##
root = true
# EditorConfig is awesome: http://EditorConfig.org
# All files
# top-most EditorConfig file
# Global settings
[*]
indent_style = space
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# Xml files
[*.xml]
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_style = space
indent_size = 2
# C# files
[*.cs]
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_style = space
indent_size = 2
#### Core EditorConfig Options ####
# Indentation and spacing
[*.{md,json}]
indent_style = space
indent_size = 4
tab_width = 4
max_line_length = 120
# New line preferences
insert_final_newline = false
#### .NET Coding Conventions ####
[*.{cs,vb}]
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
file_header_template = unset
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview?#enable-on-build
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/language-rules#option-format
# this. and Me. preferences
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# Expression-level preferences
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = true:suggestion
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Field preferences
dotnet_style_readonly_field = true:warning
# Parameter preferences
dotnet_code_quality_unused_parameters = all:suggestion
# Suppression preferences
dotnet_remove_unnecessary_suppression_exclusions = none
#### C# Coding Conventions ####
[*.cs]
indent_style = space
indent_size = 4
max_line_length = 100
# var preferences
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:suggestion
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_anonymous_function = true:suggestion
csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_prefer_readonly_struct_member = true:suggestion
# Code-block preferences
csharp_prefer_braces = true:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_namespace_declarations = file_scoped:suggestion
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_prefer_top_level_statements = true:silent
# Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
#### C# Formatting Rules ####
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options#indentation-options
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
# avoid this. unless absolutely necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# only use var when it's obvious what the variable type is
# csharp_style_var_for_built_in_types = false:none
# csharp_style_var_when_type_is_apparent = false:none
# csharp_style_var_elsewhere = false:suggestion
# use language keywords instead of BCL types
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# name all constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_style.static_prefix_style.required_prefix = s_
dotnet_naming_style.static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# Code style defaults
dotnet_sort_system_directives_first = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
# Null checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Space preferences
csharp_space_after_cast = false
@ -179,7 +139,6 @@ csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
@ -195,200 +154,6 @@ csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
[*.{cs,vb}]
# Naming rules
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.events_should_be_pascalcase.symbols = events
dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
# Symbol specifications
dotnet_naming_symbols.interfaces.applicable_kinds = interface
dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interfaces.required_modifiers =
dotnet_naming_symbols.enums.applicable_kinds = enum
dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.enums.required_modifiers =
dotnet_naming_symbols.events.applicable_kinds = event
dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.events.required_modifiers =
dotnet_naming_symbols.methods.applicable_kinds = method
dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.methods.required_modifiers =
dotnet_naming_symbols.properties.applicable_kinds = property
dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.properties.required_modifiers =
dotnet_naming_symbols.public_fields.applicable_kinds = field
dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_fields.required_modifiers =
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_fields.required_modifiers =
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_static_fields.required_modifiers = static
dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types_and_namespaces.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
dotnet_naming_symbols.type_parameters.required_modifiers =
dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_constant_fields.required_modifiers = const
dotnet_naming_symbols.local_variables.applicable_kinds = local
dotnet_naming_symbols.local_variables.applicable_accessibilities = local
dotnet_naming_symbols.local_variables.required_modifiers =
dotnet_naming_symbols.local_constants.applicable_kinds = local
dotnet_naming_symbols.local_constants.applicable_accessibilities = local
dotnet_naming_symbols.local_constants.required_modifiers = const
dotnet_naming_symbols.parameters.applicable_kinds = parameter
dotnet_naming_symbols.parameters.applicable_accessibilities = *
dotnet_naming_symbols.parameters.required_modifiers =
dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_constant_fields.required_modifiers = const
dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
dotnet_naming_symbols.local_functions.applicable_accessibilities = *
dotnet_naming_symbols.local_functions.required_modifiers =
# Naming styles
dotnet_naming_style.pascalcase.required_prefix =
dotnet_naming_style.pascalcase.required_suffix =
dotnet_naming_style.pascalcase.word_separator =
dotnet_naming_style.pascalcase.capitalization = pascal_case
dotnet_naming_style.ipascalcase.required_prefix = I
dotnet_naming_style.ipascalcase.required_suffix =
dotnet_naming_style.ipascalcase.word_separator =
dotnet_naming_style.ipascalcase.capitalization = pascal_case
dotnet_naming_style.tpascalcase.required_prefix = T
dotnet_naming_style.tpascalcase.required_suffix =
dotnet_naming_style.tpascalcase.word_separator =
dotnet_naming_style.tpascalcase.capitalization = pascal_case
dotnet_naming_style._camelcase.required_prefix = _
dotnet_naming_style._camelcase.required_suffix =
dotnet_naming_style._camelcase.word_separator =
dotnet_naming_style._camelcase.capitalization = camel_case
dotnet_naming_style.camelcase.required_prefix =
dotnet_naming_style.camelcase.required_suffix =
dotnet_naming_style.camelcase.word_separator =
dotnet_naming_style.camelcase.capitalization = camel_case
dotnet_naming_style.s_camelcase.required_prefix = s_
dotnet_naming_style.s_camelcase.required_suffix =
dotnet_naming_style.s_camelcase.word_separator =
dotnet_naming_style.s_camelcase.capitalization = camel_case
##################################################################################
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
@ -410,7 +175,7 @@ dotnet_diagnostic.CA1304.severity = error
# CA1307: Specify StringComparison for clarity
dotnet_diagnostic.CA1307.severity = error
# CA1308: Normalize strings to uppercase
dotnet_diagnostic.CA1308.severity = none
dotnet_diagnostic.CA1308.severity = error
# CA1309: Use ordinal StringComparison
dotnet_diagnostic.CA1309.severity = error
# CA1724: Type names should not match namespaces
@ -445,18 +210,7 @@ dotnet_diagnostic.ca1848.severity = Suggestion
dotnet_diagnostic.ca1810.severity = Suggestion
# CA1725: Parameter names should match base declaration
dotnet_diagnostic.ca1725.severity = Suggestion
# CA1515: Consider making public types internal
dotnet_diagnostic.CA1515.severity = None
# CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = Suggestion
# CA1707: Identifiers should not contain underscores
dotnet_diagnostic.CA1707.severity = None
# CA1716: Identifiers should not match keywords
dotnet_diagnostic.CA1716.severity = Suggestion
# CA1032: Implement standard exception constructors
dotnet_diagnostic.CA1032.severity = Suggestion
# AV1500: A method should not exceed a predefined number (60-100 lines) of lines
dotnet_diagnostic.AV1500.severity = none
# https://csharpier.com/docs/IntegratingWithLinters#code-analysis-rules
##################################################################################
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
@ -466,7 +220,7 @@ dotnet_diagnostic.AV1500.severity = none
dotnet_diagnostic.IDE0048.severity = Suggestion
dotnet_diagnostic.IDE0028.severity = Suggestion
dotnet_diagnostic.IDE0029.severity = Suggestion
dotnet_diagnostic.IDE0030 .severity = Suggestion
dotnet_diagnostic.IDE0030.severity = Suggestion
dotnet_diagnostic.IDE0004.severity = error
# IDE0005: Remove unnecessary usings/imports
@ -476,21 +230,16 @@ dotnet_diagnostic.IDE0005.severity = warning
dotnet_diagnostic.IDE0051.severity = Suggestion
# IDE0052: Remove unread private members (writes but no reads)
dotnet_diagnostic.IDE0052.severity = warning
# Remove unnecessary using directives (IDE0005)
dotnet_diagnostic.IDE0005.severity = none
# CS1574: XML comment on 'construct' has syntactically incorrect cref attribute 'name'
dotnet_diagnostic.CS1574.severity = error
dotnet_diagnostic.IDE0052.severity = error
# IDE0055: Fix formatting
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/dotnet-formatting-options
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
dotnet_diagnostic.IDE0055.severity = suggestion
# https://csharpier.com/docs/IntegratingWithLinters#code-analysis-rules
dotnet_diagnostic.IDE0055.severity = none
# CS1574: XML comment on 'construct' has syntactically incorrect cref attribute 'name'
dotnet_diagnostic.CS1574.severity = error
##################################################################################
# https://jetbrains.com.xy2401.com/help/resharper/EditorConfig_Index.html
@ -624,6 +373,59 @@ resharper_blank_lines_before_multiline_statements = 1
resharper_parentheses_non_obvious_operations = arithmetic, multiplicative, equality, relational, additive
resharper_parentheses_redundancy_style = remove_if_not_clarifies_precedence
##################################################################################
## https://github.com/bkoelman/CSharpGuidelinesAnalyzer
## CSharpGuidelines
##################################################################################
dotnet_diagnostic.AV1561.max_parameter_count = 5
# AV1008: Class should be non-static or its name should be suffixed with Extensions
dotnet_diagnostic.AV1008.severity = none
# AV1010: Type hides inherited member
dotnet_diagnostic.AV1010.severity = none
# AV1115: Member or local function contains the word 'and', which suggests doing multiple things
dotnet_diagnostic.AV1115.severity = suggestion
# AV1130: Return type in signature for Type should be a collection interface instead of a concrete type
dotnet_diagnostic.AV1130.severity = none
# AV1135: null is returned from method which has return type of string, collection or task
dotnet_diagnostic.AV1135.severity = none # re-enable if we can distinguish between string, collection and task
# AV1210: Catch a specific exception instead of Exception, SystemException or ApplicationException
dotnet_diagnostic.AV1210.severity = none
# AV1250: Evaluate LINQ query before returning it
dotnet_diagnostic.AV1250.severity = suggestion
# AV1500: Method 'CallerIdentifier.DetermineCallerIdentity()' contains 10 statements, which exceeds the maximum of 7 statements
dotnet_diagnostic.AV1500.severity = none
# AV1532: Loop statement contains nested loop
dotnet_diagnostic.AV1532.severity = suggestion
# AV1535: Missing block in case or default clause of switch statement
dotnet_diagnostic.AV1535.severity = none # re-enable if we can adjust the formatting to not indent the scope braces
# AV1537: If-else-if construct should end with an unconditional else clause
dotnet_diagnostic.AV1537.severity = suggestion
# AV1551: Method overload with the most parameters should be virtual
dotnet_diagnostic.AV1551.severity = none
# AV1555: Avoid using non-(nullable-)boolean named arguments
dotnet_diagnostic.AV1555.severity = suggestion
# AV1561: Method contains 5 parameters, which exceeds the maximum of 3 parameters
dotnet_diagnostic.AV1561.severity = suggestion
# AV1564: Parameter in public or internal member is of type bool or bool?
dotnet_diagnostic.AV1564.severity = suggestion
# AV1554: Do not use optional parameters in interface methods or their concrete implementations
dotnet_diagnostic.AV1554.severity = none
# AV1580: Argument for parameter calls nested method
dotnet_diagnostic.AV1580.severity = none
# AV1706: Parameter 'p' should have a more descriptive name
dotnet_diagnostic.AV1706.severity = warning
# AV1708: Type name contains term that should be avoided
dotnet_diagnostic.AV1708.severity = suggestion
# AV1710: Field contains the name of its containing type
dotnet_diagnostic.AV1710.severity = none
# AV2202: Replace call to Nullable<T>.HasValue with null check
dotnet_diagnostic.AV2202.severity = none
# AV2305: Missing XML comment for internally visible type or member
dotnet_diagnostic.AV2305.severity = none
# AV2407: Region should be removed
dotnet_diagnostic.AV2407.severity = none
##################################################################################
## https://github.com/DotNetAnalyzers/StyleCopAnalyzers/tree/master/documentation
## https://documentation.help/StyleCop/StyleCop.html
@ -746,85 +548,16 @@ dotnet_diagnostic.sa1101.severity = None
# The keywords within the declaration of an element do not follow a standard ordering scheme.
dotnet_diagnostic.SA1206.severity = None
dotnet_diagnostic.SA1106.severity = None
# https://csharpier.com/docs/IntegratingWithLinters#stylecopanalyzers
# IDE0055: Fix formatting
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/dotnet-formatting-options
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
# StyleCopAnalyzers
dotnet_diagnostic.SA1000.severity = none
dotnet_diagnostic.SA1009.severity = none
dotnet_diagnostic.SA1111.severity = none
dotnet_diagnostic.SA1118.severity = none
dotnet_diagnostic.SA1137.severity = none
dotnet_diagnostic.SA1413.severity = none
dotnet_diagnostic.SA1500.severity = none
dotnet_diagnostic.SA1501.severity = none
dotnet_diagnostic.SA1502.severity = none
dotnet_diagnostic.SA1504.severity = none
dotnet_diagnostic.SA1515.severity = none
dotnet_diagnostic.SA1516.severity = none
# for csharpier <= 0.21.0
dotnet_diagnostic.SA1127.severity = none
dotnet_diagnostic.SA1128.severity = none
dotnet_diagnostic.SA1001.severity = none
dotnet_diagnostic.SA1002.severity = none
dotnet_diagnostic.SA1003.severity = none
dotnet_diagnostic.SA1007.severity = none
dotnet_diagnostic.SA1008.severity = none
dotnet_diagnostic.SA1010.severity = none
dotnet_diagnostic.SA1011.severity = none
dotnet_diagnostic.SA1012.severity = none
dotnet_diagnostic.SA1013.severity = none
dotnet_diagnostic.SA1014.severity = none
dotnet_diagnostic.SA1015.severity = none
dotnet_diagnostic.SA1016.severity = none
dotnet_diagnostic.SA1017.severity = none
dotnet_diagnostic.SA1018.severity = none
dotnet_diagnostic.SA1019.severity = none
dotnet_diagnostic.SA1020.severity = none
dotnet_diagnostic.SA1021.severity = none
dotnet_diagnostic.SA1022.severity = none
dotnet_diagnostic.SA1023.severity = none
dotnet_diagnostic.SA1024.severity = none
dotnet_diagnostic.SA1025.severity = none
dotnet_diagnostic.SA1026.severity = none
dotnet_diagnostic.SA1027.severity = none
dotnet_diagnostic.SA1028.severity = none
dotnet_diagnostic.SA1102.severity = none
dotnet_diagnostic.SA1103.severity = none
dotnet_diagnostic.SA1104.severity = none
dotnet_diagnostic.SA1105.severity = none
dotnet_diagnostic.SA1107.severity = none
dotnet_diagnostic.SA1110.severity = none
dotnet_diagnostic.SA1112.severity = none
dotnet_diagnostic.SA1113.severity = none
dotnet_diagnostic.SA1114.severity = none
dotnet_diagnostic.SA1115.severity = none
dotnet_diagnostic.SA1116.severity = none
dotnet_diagnostic.SA1117.severity = none
dotnet_diagnostic.SA1127.severity = none
dotnet_diagnostic.SA1128.severity = none
dotnet_diagnostic.SA1136.severity = none
dotnet_diagnostic.SA1505.severity = none
dotnet_diagnostic.SA1506.severity = none
dotnet_diagnostic.SA1507.severity = none
dotnet_diagnostic.SA1508.severity = none
dotnet_diagnostic.SA1509.severity = none
dotnet_diagnostic.SA1510.severity = none
dotnet_diagnostic.SA1511.severity = none
dotnet_diagnostic.SA1517.severity = none
dotnet_diagnostic.SA1518.severity = none
# A single-line comment within C# code is not preceded by a blank line.
dotnet_diagnostic.SA1515.severity = Suggestion
##################################################################################
## https://github.com/meziantou/Meziantou.Analyzer/tree/main/docs
## Meziantou.Analyzer
# MA0049: Type name should not match containing namespace
dotnet_diagnostic.ma0049.severity = Suggestion
# MA0048: File name must match type name
dotnet_diagnostic.ma0048.severity = Suggestion
@ -878,14 +611,11 @@ dotnet_diagnostic.MA0047.severity = none
# Use an overload of 'GetHashCode' that has a StringComparison parameter
dotnet_diagnostic.MA0074.severity = none
# MA0049: Type name should not match containing namespace
dotnet_diagnostic.MA0049.severity = none
##################################################################################
## http://pihrt.net/Roslynator/Analyzers
## http://pihrt.net/Roslynator/Refactorings
## https://github.com/JosefPihrt/Roslynator/blob/main/docs/Configuration.md
## https://josefpihrt.github.io/docs/
## Roslynator
##################################################################################
# RCS1036 - Remove redundant empty line.
dotnet_diagnostic.rcs1036.severity = None
@ -920,6 +650,7 @@ dotnet_diagnostic.rcs1047.severity = error
# RCS1174: Remove redundant async/await
dotnet_diagnostic.rcs1174.severity = Suggestion
# Combine 'Enumerable.Where' method chain. It doesn't make it more readable in all cases.
dotnet_diagnostic.RCS1112.severity = suggestion
@ -957,30 +688,6 @@ dotnet_diagnostic.RCS1237.severity = none
# RCS1228: Unused element in documentation comment. (Equivalent to SA1614)
dotnet_diagnostic.RCS1228.severity = suggestion
# RCS1047: Non-asynchronous method name should not end with 'Async'
#dotnet_diagnostic.RCS1047.severity = suggestion
##################################################################################
## https://github.com/semihokur/asyncfixer
## AsyncFixer01
##################################################################################
# https://cezarypiatek.github.io/post/async-analyzers-p1/#1-redundant-asyncawait
# AsyncFixer01: Unnecessary async/await usage
dotnet_diagnostic.asyncfixer01.severity = Suggestion
# https://cezarypiatek.github.io/post/async-analyzers-p1/#2-calling-synchronous-method-inside-the-async-method
# AsyncFixer02: Long-running or blocking operations inside an async method
dotnet_diagnostic.asyncfixer02.severity = error
# https://cezarypiatek.github.io/post/async-analyzers-p1/#3-async-void-method
# AsyncFixer03: Fire & forget async void methods
dotnet_diagnostic.asyncfixer03.severity = error
# https://cezarypiatek.github.io/post/async-analyzers-p1/#6-not-awaited-task-inside-the-using-block
# AsyncFixer04: Fire & forget async call inside a using block
dotnet_diagnostic.asyncfixer04.severity = error
##################################################################################
## https://github.com/microsoft/vs-threading
## Microsoft.VisualStudio.Threading.Analyzers
@ -990,6 +697,7 @@ dotnet_diagnostic.asyncfixer04.severity = error
# VSTHRD103: Call async methods when in an async method
dotnet_diagnostic.vsthrd103.severity = Suggestion
# https://cezarypiatek.github.io/post/async-analyzers-p1/#3-async-void-method
# VSTHRD100: Avoid async void methods
dotnet_diagnostic.vsthrd100.severity = error
@ -1025,20 +733,3 @@ dotnet_diagnostic.vsthrd200.severity = Suggestion
# https://cezarypiatek.github.io/post/async-analyzers-p2/#12-non-asynchronous-method-names-shouldnt-end-with-async
# VSTHRD200: Use "Async" suffix for async methods
dotnet_diagnostic.vsthrd200.severity = Suggestion
# VSTHRD003 Avoid awaiting foreign Tasks
dotnet_diagnostic.VSTHRD003.severity = Suggestion
##################################################################################
## https://github.com/hvanbakel/Asyncify-CSharp
## Asyncify
##################################################################################
# https://cezarypiatek.github.io/post/async-analyzers-p2/#8-synchronous-waits
# AsyncifyInvocation: Use Task Async
dotnet_diagnostic.asyncifyinvocation.severity = error
# https://cezarypiatek.github.io/post/async-analyzers-p2/#8-synchronous-waits
# AsyncifyVariable: Use Task Async
dotnet_diagnostic.asyncifyvariable.severity = error

64
.gitattributes vendored
View File

@ -1,64 +0,0 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
*.sh text eol=lf
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

View File

@ -25,16 +25,14 @@ runs:
# https://devblogs.microsoft.com/dotnet/dotnet-loves-github-actions/
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net#caching-dependencies
- name: Cache NuGet Packages
uses: actions/cache@v4
uses: actions/cache@v3
if: success()
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-dotnet-nuget
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.x.x'
uses: actions/setup-dotnet@v3
# https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools
- name: Restore .NET Tools

View File

@ -18,9 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Build and Test Flight Microservice
- name: Build and Test Flight
uses: ./.github/actions/build-test
if: success()
id: build-test-flight-step
@ -35,7 +35,7 @@ jobs:
service-name: 'Flight'
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Test Identity Microservice
- name: Build and Test Identity
uses: ./.github/actions/build-test
if: success()
id: build-test-identity-step
@ -50,7 +50,7 @@ jobs:
service-name: 'Identity'
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Test Passenger Microservice
- name: Build and Test Passenger
uses: ./.github/actions/build-test
if: success()
id: build-test-passenger-step
@ -65,7 +65,7 @@ jobs:
service-name: 'Passenger'
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Test Booking Microservice
- name: Build and Test Booking
uses: ./.github/actions/build-test
if: success()
id: build-test-booking-step
@ -91,7 +91,7 @@ jobs:
run:
echo "Release version is:" ${{ steps.last_release.outputs.tag_name }}
- name: Build and Publish Identity Microservice to Docker
- name: Build and Publish Identity to Docker
if: ${{ github.ref == 'refs/heads/main' && success() }}
uses: ./.github/actions/docker-build-publish
with:
@ -101,7 +101,7 @@ jobs:
dockerfile-path: 'src/Services/Identity/Dockerfile'
image-name: 'booking-microservices-identity'
- name: Build and Publish Flight Microservice to Docker
- name: Build and Publish Flight to Docker
if: ${{ github.ref == 'refs/heads/main' && success() }}
uses: ./.github/actions/docker-build-publish
with:
@ -111,7 +111,7 @@ jobs:
dockerfile-path: 'src/Services/Flight/Dockerfile'
image-name: 'booking-microservices-flight'
- name: Build and Publish Passenger Microservice to Docker
- name: Build and Publish Passenger to Docker
if: ${{ github.ref == 'refs/heads/main' && success() }}
uses: ./.github/actions/docker-build-publish
with:
@ -121,7 +121,7 @@ jobs:
dockerfile-path: 'src/Services/Passenger/Dockerfile'
image-name: 'booking-microservices-passenger'
- name: Build and Publish Booking Microservice to Docker
- name: Build and Publish Booking to Docker
if: ${{ github.ref == 'refs/heads/main' && success() }}
uses: ./.github/actions/docker-build-publish
with:

1
.gitpod.Dockerfile vendored Normal file
View File

@ -0,0 +1 @@
FROM gitpod/workspace-dotnet:latest

37
.gitpod.yml Normal file
View File

@ -0,0 +1,37 @@
# https://github.com/gitpod-samples/template-dotnet-core-cli-csharp
# https://www.gitpod.io/docs/introduction/languages/dotnet
# https://github.com/gitpod-samples/template-docker-compose
# https://www.gitpod.io/docs/references/gitpod-yml
# https://www.gitpod.io/docs/configure
# https://www.gitpod.io/docs/configure/workspaces/ports
image:
file: .gitpod.Dockerfile
vscode:
extensions:
- muhammad-sammy.csharp
- editorconfig.editorconfig
- vivaxy.vscode-conventional-commits
- humao.rest-client
- ms-azuretools.vscode-docker
- donjayamanne.githistory
- pkief.material-icon-theme
- emmanuelbeziat.vscode-great-icons
# https://www.gitpod.io/docs/configure/workspaces/tasks#execution-order
# https://www.gitpod.io/docs/configure/projects/prebuilds
tasks:
- name: Init Docker-Compose
# https://www.gitpod.io/docs/configure/projects/prebuilds
# We load docker on pre-build for increasing speed
init: |
docker-compose pull
docker-compose -f ./deployments/docker-compose/infrastracture.yaml up -d
- name: Setup kubectl
command: bash $GITPOD_REPO_ROOT/scripts/setup_kubectl_gitpod.sh
- name: Restore & Build
init: |
dotnet dev-certs https
dotnet restore
dotnet build

View File

@ -1,2 +1 @@
npm run format
npm run ci-format
npm run format

View File

@ -1,35 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" Version="1.1.118">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" Version="2.0.299">
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" Version="2.0.163">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" Version="4.15.0">
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" Version="4.12.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" PrivateAssets="all" Version="4.15.0">
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" PrivateAssets="all" Version="4.12.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="all" Version="4.15.0">
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="all" Version="4.12.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="all" Version="17.14.15">
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="all" Version="17.11.20">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="AsyncAwaitBestPractices" PrivateAssets="all" Version="10.0.0">
<PackageReference Include="AsyncAwaitBestPractices" PrivateAssets="all" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="all" Version="0.15.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="CSharpGuidelinesAnalyzer" PrivateAssets="all" Version="3.8.5">

131
README.md
View File

@ -6,12 +6,13 @@
</div>
</div>
> 🚀 **A practical microservices with the latest technologies and architectures like Vertical Slice Architecture, Event Sourcing, CQRS, DDD, gRpc, MongoDB, RabbitMq, Masstransit, and Aspire in .Net 10.**
> 🚀 **A practical and imaginary microservices for implementing an infrastructure for up and running distributed system with the latest technology and architecture like Vertical Slice Architecture, Event Sourcing, CQRS, DDD, gRpc, MongoDB, RabbitMq, Masstransit in .Net 8.**
## You can find other version of this project here:
- [Booking with Modular Monolith Architecture](https://github.com/meysamhadeli/booking-modular-monolith)
- [Booking with Monolith Architecture](https://github.com/meysamhadeli/booking-monolith)
> 💡 **This project is not business-oriented and most of my focus was in the thechnical part for implement a distributed system with a sample project. In this project I implemented some concept in microservices like Messaging, Tracing, Event Driven Architecture, Vertical Slice Architecture, Event Sourcing, CQRS, DDD and gRpc.**
<div>
<a href="https://gitpod.io/#https://github.com/meysamhadeli/booking-microservices"><img alt="Open in Gitpod" src="https://gitpod.io/button/open-in-gitpod.svg"/></a>
</div>
<div>
<a href='https://codespaces.new/meysamhadeli/booking-microservices?quickstart=1'><img alt='Open in GitHub Codespaces' src='https://github.com/codespaces/badge.svg'></a>
</div>
@ -19,19 +20,16 @@
# Table of Contents
- [The Goals of This Project](#the-goals-of-this-project)
- [Plan](#plan)
- [Technologies - Libraries](#technologies---libraries)
- [Key Features](#key-features)
- [When to Use](#when-to-use)
- [Challenges](#challenges)
- [The Domain and Bounded Context - Service Boundary](#the-domain-and-bounded-context---service-boundary)
- [Structure of Project](#structure-of-project)
- [Development Setup](#development-setup)
- [Dotnet Tools Packages](#dotnet-tools-packages)
- [Husky](#husky)
- [Upgrade Nuget Packages](#upgrade-nuget-packages)
- [Dotnet Tools Packages](#dotnet-tools-packages)
- [Husky](#husky)
- [Upgrade Nuget Packages](#upgrade-nuget-packages)
- [How to Run](#how-to-run)
- [Config Certificate](#config-certificate)
- [Aspire](#aspire)
- [Docker Compose](#docker-compose)
- [Kubernetes](#kubernetes)
- [Build](#build)
@ -41,24 +39,22 @@
- [Support](#support)
- [Contribution](#contribution)
## The Goals of This Project
- :sparkle: Using `Vertical Slice Architecture` for `architecture` level.
- :sparkle: Using `Domain Driven Design (DDD)` to implement all `business logic`.
- :sparkle: Using `Rabbitmq` on top of `Masstransit` for `Event Driven Architecture`.
- :sparkle: Using `gRPC` for `internal communication`.
- :sparkle: Using `Domain Driven Design (DDD)` to implement all `business processes` in microservices.
- :sparkle: Using `Rabbitmq` on top of `Masstransit` for `Event Driven Architecture` between our microservices.
- :sparkle: Using `gRPC` for `internal communication` between our microservices.
- :sparkle: Using `CQRS` implementation with `MediatR` library.
- :sparkle: Using `Postgres` for `write side` database.
- :sparkle: Using `MongoDB` for `read side` database.
- :sparkle: Using `Event Store` for `write side` of Booking Microservice/Module to store all `historical change` of aggregate.
- :sparkle: Using `Postgres` for `write side` of some microservices.
- :sparkle: Using `MongoDB` for `read side` of some microservices.
- :sparkle: Using `Event Store` for `write side` of Booking-Microservice to store all `historical state` of aggregate.
- :sparkle: Using `Inbox Pattern` for ensuring message idempotency for receiver and `Exactly once Delivery`.
- :sparkle: Using `Outbox Pattern` for ensuring no message is lost and there is at `At Least One Delivery`.
- :sparkle: Using `Unit Testing` for testing small units and mocking our dependencies with `Nsubstitute`.
- :sparkle: Using `End-To-End Testing` and `Integration Testing` for testing `features` with all dependencies using `testcontainers`.
- :sparkle: Using `Fluent Validation` and a `Validation Pipeline Behaviour` on top of `MediatR`.
- :sparkle: Using `Minimal API` for all endpoints.
- :sparkle: Using `AspNetCore OpenApi` for `generating` built-in support `OpenAPI documentation` in ASP.NET Core.
- :sparkle: Using `Health Check` for `reporting` the `health` of app infrastructure components.
- :sparkle: Using `Docker-Compose` and `Kubernetes` for our deployment mechanism.
- :sparkle: Using `Kibana` on top of `Serilog` for `logging`.
@ -69,31 +65,44 @@
- :sparkle: Using `Kubernetes` to achieve efficient `scaling` and ensure `high availability` for each of our microservices.
- :sparkle: Using `Nginx Ingress Controller` for `load balancing` between our microservices top of `Kubernetes`.
- :sparkle: Using `cert-manager` to Configure `TLS` in `kubernetes cluster`.
- :sparkle: Using `Aspire` for `service discovery`, `observability`, and `local orchestration` of microservices.
## Plan
> 🌀This project is a work in progress, new features will be added over time.🌀
I will try to register future goals and additions in the [Issues](https://github.com/meysamhadeli/booking-microservices/issues) section of this repository.
High-level plan is represented in the table
| Feature | Status |
| ----------------- | -------------- |
| API Gateway | Completed ✔️ |
| Identity Service | Completed ✔️ |
| Flight Service | Completed ✔️ |
| Passenger Service | Completed ✔️ |
| Booking Service | Completed ✔️ |
| Building Blocks | Completed ✔️ |
## Technologies - Libraries
- ✔️ **[`.NET 10`](https://github.com/dotnet/aspnetcore)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core.
- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core.
- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
- ✔️ **[`AspNetCore OpenApi`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi)** - Provides built-in support for OpenAPI document generation in ASP.NET Core.
- ✔️ **[`.NET 7`](https://dotnet.microsoft.com/download)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core
- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core
- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations
- ✔️ **[`Masstransit`](https://github.com/MassTransit/MassTransit)** - Distributed Application Framework for .NET.
- ✔️ **[`MediatR`](https://github.com/jbogard/MediatR)** - Simple, unambitious mediator implementation in .NET.
- ✔️ **[`FluentValidation`](https://github.com/FluentValidation/FluentValidation)** - Popular .NET validation library for building strongly-typed validation rules.
- ✔️ **[`Scalar`](https://github.com/scalar/scalar/tree/main/packages/scalar.aspnetcore)** - Scalar provides an easy way to render beautiful API references based on OpenAPI/Swagger documents.
- ✔️ **[`Swagger UI`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)** - Swagger tools for documenting API's built on ASP.NET Core.
- ✔️ **[`FluentValidation`](https://github.com/FluentValidation/FluentValidation)** - Popular .NET validation library for building strongly-typed validation rules
- ✔️ **[`Swagger & Swagger UI`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)** - Swagger tools for documenting API's built on ASP.NET Core
- ✔️ **[`Serilog`](https://github.com/serilog/serilog)** - Simple .NET logging with fully-structured events
- ✔️ **[`Polly`](https://github.com/App-vNext/Polly)** - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
- ✔️ **[`Polly`](https://github.com/App-vNext/Polly)** - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner
- ✔️ **[`Scrutor`](https://github.com/khellang/Scrutor)** - Assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection
- ✔️ **[`Opentelemetry-dotnet`](https://github.com/open-telemetry/opentelemetry-dotnet)** - The OpenTelemetry .NET Client
- ✔️ **[`DuendeSoftware IdentityServer`](https://github.com/DuendeSoftware/IdentityServer)** - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core.
- ✔️ **[`DuendeSoftware IdentityServer`](https://github.com/DuendeSoftware/IdentityServer)** - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core
- ✔️ **[`EasyCaching`](https://github.com/dotnetcore/EasyCaching)** - Open source caching library that contains basic usages and some advanced usages of caching which can help us to handle caching more easier.
- ✔️ **[`Mapster`](https://github.com/MapsterMapper/Mapster)** - Convention-based object-object mapper in .NET.
- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core.
- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs.
- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET.
- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier.
- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core
- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs
- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET
- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier
- ✔️ **[`gRPC-dotnet`](https://github.com/grpc/grpc-dotnet)** - gRPC functionality for .NET.
- ✔️ **[`EventStore`](https://github.com/EventStore/EventStore)** - The open-source, functional database with Complex Event Processing.
- ✔️ **[`MongoDB.Driver`](https://github.com/mongodb/mongo-csharp-driver)** - .NET Driver for MongoDB.
@ -101,29 +110,6 @@
- ✔️ **[`Respawn`](https://github.com/jbogard/Respawn)** - Respawn is a small utility to help in resetting test databases to a clean state.
- ✔️ **[`Testcontainers`](https://github.com/testcontainers/testcontainers-dotnet)** - Testcontainers for .NET is a library to support tests with throwaway instances of Docker containers.
- ✔️ **[`K6`](https://github.com/grafana/k6)** - Modern load testing for developers and testers in the DevOps era.
- ✔️ **[`Aspire`](https://github.com/dotnet/aspire)** - .NET stack for building and orchestrating observable, distributed cloud-native applications.
## Key Features
1. **Independent Services**: Each service is a separate project with its own database and deployment pipeline, enabling independent development and deployment.
2. **Decentralized Communication**: Services communicate via APIs (REST, gRPC) or message brokers (RabbitMQ, Kafka), ensuring loose coupling and resilience.
3. **Scalability**: Services can be scaled independently based on demand, allowing efficient resource utilization.
4. **Fault Tolerance**: Failures are isolated, preventing cascading failures and ensuring high availability.
5. **Technology Agnostic**: Services can use different technologies, frameworks, or databases, providing flexibility.
## When to Use
1. **Large and Complex Projects**: Ideal for applications with complex business logic that can be broken into smaller, manageable services.
2. **High Scalability Needs**: Suitable for applications requiring independent scaling of components.
3. **Fault Tolerance and High Availability**: Perfect for systems where failure isolation and uptime are critical.
4. **Distributed Teams**: Enables teams to work independently on different services.
5. **Frequent Updates**: Supports continuous deployment and A/B testing for individual services.
6. **Technology Diversity**: Allows the use of different technologies for different services.
## Challenges
- Increased complexity in management, DevOps overhead, data consistency, latency, and higher costs.
## The Domain And Bounded Context - Service Boundary
@ -137,10 +123,19 @@
![](./assets/booking-microservices.png)
## Structure of Project
In this project, I used [vertical slice architecture](https://jimmybogard.com/vertical-slice-architecture/) at the architectural level and [feature folder structure](http://www.kamilgrzybek.com/design/feature-folders/) to structure my files.
In this project I used a mix of [clean architecture](https://jasontaylor.dev/clean-architecture-getting-started/), [vertical slice architecture](https://jimmybogard.com/vertical-slice-architecture/) and I used [feature folder structure](http://www.kamilgrzybek.com/design/feature-folders/) to structure my files.
I used [yarp reverse proxy](https://microsoft.github.io/reverse-proxy/articles/index.html) to route synchronous and asynchronous requests to the corresponding microservice. Each microservice has its dependencies such as databases, files etc. Each microservice is decoupled from other microservices and developed and deployed separately. Microservices talk to each other with Rest or gRPC for synchronous calls and use RabbitMq or Kafka for asynchronous calls.
We have a separate microservice ([IdentityServer](https://github.com/DuendeSoftware/IdentityServer)) for authentication and authorization of each request. Once signed-in users are issued a JWT token. This token is used by other microservices to validate the user, read claims and allow access to authorized/role specific endpoints.
I used [RabbitMQ](https://github.com/rabbitmq) as my MessageBroker for async communication between microservices using the eventual consistency mechanism. Each microservice uses [MassTransit](https://github.com/MassTransit/MassTransit) to interface with [RabbitMQ](https://github.com/rabbitmq) providing, messaging, availability, reliability, etc.
Microservices are `event based` which means they can publish and/or subscribe to any events occurring in the setup. By using this approach for communicating between services, each microservice does not need to know about the other services or handle errors occurred in other microservices.
After saving data in write side, I save a [Internal Command](https://github.com/kgrzybek/modular-monolith-with-ddd#38-internal-processing) record in my Persist Messages storage (like something we do in outbox pattern) and after committing transaction in write side, trigger our command handler in read side and this handler could save their read models in our MongoDB database.
I treat each request as a distinct use case or slice, encapsulating and grouping all concerns from front-end to back.
When adding or changing a feature in an application in n-tire architecture, we are typically touching many "layers" in an application. We are changing the user interface, adding fields to models, modifying validation, and so on. Instead of coupling across a layer, we couple vertically along a slice. We `minimize coupling` `between slices`, and `maximize coupling` `in a slice`.
@ -163,7 +158,6 @@ I used CQRS to decompose my features into small parts that makes our application
Using the CQRS pattern, we cut each business functionality into vertical slices, for each of these slices we group classes (see [technical folders structure](http://www.kamilgrzybek.com/design/feature-folders)) specific to that feature together (command, handlers, infrastructure, repository, controllers, etc). In our CQRS pattern each command/query handler is a separate slice. This is where you can reduce coupling between layers. Each handler can be a separated code unit, even copy/pasted. Thanks to that, we can tune down the specific method to not follow general conventions (e.g. use custom SQL query or even different storage). In a traditional layered architecture, when we change the core generic mechanism in one layer, it can impact all methods.
## Development Setup
### Dotnet Tools Packages
@ -177,8 +171,8 @@ dotnet tool restore
```
### Husky
Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](.././package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html))
We need to install `husky` package for `manage` `pre commits hooks` and also I add two packages `@commitlint/cli` and `@commitlint/config-conventional` for handling conventional commits rules in [package.json](.././package.json).
Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](./package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html))
We need to install `husky` package for `manage` `pre commits hooks` and also I add two packages `@commitlint/cli` and `@commitlint/config-conventional` for handling conventional commits rules in [package.json](./package.json).
Run the command bellow in the root of project to install all npm dependencies related to husky:
```bash
@ -204,24 +198,13 @@ Run the following commands to [Config SSL](https://docs.microsoft.com/en-us/aspn
dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p password
dotnet dev-certs https --trust
```
> Note: for running this command in `powershell` use `$env:USERPROFILE` instead of `%USERPROFILE%`*
***Note:** for running this command in `powershell` use `$env:USERPROFILE` instead of `%USERPROFILE%`*
#### macOS or Linux
```bash
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p $CREDENTIAL_PLACEHOLDER$
dotnet dev-certs https --trust
```
### Aspire
To run the application using the `Aspire App Host`, execute the following command from the solution root:
```bash
aspire run
```
> Note:The `Aspire dashboard` will be available at `http://localhost:18888`
> ### Docker Compose
@ -265,7 +248,7 @@ dotnet test
> ### Documentation Apis
Each microservice provides `API documentation` and navigate to `/swagger` for `Swagger OpenAPI` or `/scalar/v1` for `Scalar OpenAPI` to visit list of endpoints.
Each microservice has a `Swagger OpenAPI`. Browse to `/swagger` for a list of endpoints.
As part of API testing, I created the [booking.rest](./booking.rest) file which can be run with the [REST Client](https://github.com/Huachao/vscode-restclient) `VSCode plugin`.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 643 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1,175 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{5B69EDFD-4B09-457A-AAAF-D816D402D595}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{9010E0B5-9C42-4256-ADE4-E290434F2CEF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiGateway", "ApiGateway", "{3E38DD17-9EEE-4815-9D5B-BEB5549020A0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{776BDF43-0DEA-44A3-AF72-99408CE544EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiGateway", "src\ApiGateway\src\ApiGateway.csproj", "{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildingBlocks", "src\BuildingBlocks\BuildingBlocks.csproj", "{E42BB533-4144-4D78-BCCE-50BA00BCADBE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Flight", "Flight", "{5F0996AB-F8DB-4240-BD4A-DFDD70638A73}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Passenger", "Passenger", "{1A2ABCD9-493B-4848-9C69-919CDBCA61F3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Booking", "Booking", "{22447274-717D-4321-87F3-868BAF93CBEC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{55BE6759-95AA-434D-925D-A8D32F274E66}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E2637D6D-04A5-4DE4-8AAF-E015C65DE8E1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{5185D5C5-0EAD-49D5-B405-93B939F3639B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{53D0AA09-F5FA-4721-8C1B-375CBD15B4E8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C6EE337B-91EA-472A-87C7-E9528408CE59}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F39D8F09-6233-4495-ACD0-F98904993B7E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{295284BA-D4E4-40AA-A2C2-BE36343F7DE6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{85DA00E5-CC11-463C-8577-C34967C328F7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C1EBE17D-BFAD-47DA-88EB-BB073B84593E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking", "src\Services\Booking\src\Booking\Booking.csproj", "{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking.Api", "src\Services\Booking\src\Booking.Api\Booking.Api.csproj", "{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flight", "src\Services\Flight\src\Flight\Flight.csproj", "{574222F8-9C26-4015-8F35-C1E5D41A505F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flight.Api", "src\Services\Flight\src\Flight.Api\Flight.Api.csproj", "{B8F734F5-873C-4367-9EBD-38EA420CD868}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity", "src\Services\Identity\src\Identity\Identity.csproj", "{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.Api", "src\Services\Identity\src\Identity.Api\Identity.Api.csproj", "{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Passenger", "src\Services\Passenger\src\Passenger\Passenger.csproj", "{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Passenger.Api", "src\Services\Passenger\src\Passenger.Api\Passenger.Api.csproj", "{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Flight\tests\IntegrationTest\Integration.Test.csproj", "{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Identity\tests\IntegrationTest\Integration.Test.csproj", "{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Passenger\tests\IntegrationTest\Integration.Test.csproj", "{539364C8-88B1-48A3-8406-D0B19FF30509}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Booking\tests\IntegrationTest\Integration.Test.csproj", "{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit.Test", "src\Services\Flight\tests\UnitTest\Unit.Test.csproj", "{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EndToEnd.Test", "src\Services\Flight\tests\EndToEndTest\EndToEnd.Test.csproj", "{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{776BDF43-0DEA-44A3-AF72-99408CE544EE} = {3E38DD17-9EEE-4815-9D5B-BEB5549020A0}
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5} = {776BDF43-0DEA-44A3-AF72-99408CE544EE}
{E42BB533-4144-4D78-BCCE-50BA00BCADBE} = {5B69EDFD-4B09-457A-AAAF-D816D402D595}
{5F0996AB-F8DB-4240-BD4A-DFDD70638A73} = {9010E0B5-9C42-4256-ADE4-E290434F2CEF}
{1A2ABCD9-493B-4848-9C69-919CDBCA61F3} = {9010E0B5-9C42-4256-ADE4-E290434F2CEF}
{22447274-717D-4321-87F3-868BAF93CBEC} = {9010E0B5-9C42-4256-ADE4-E290434F2CEF}
{55BE6759-95AA-434D-925D-A8D32F274E66} = {9010E0B5-9C42-4256-ADE4-E290434F2CEF}
{E2637D6D-04A5-4DE4-8AAF-E015C65DE8E1} = {22447274-717D-4321-87F3-868BAF93CBEC}
{5185D5C5-0EAD-49D5-B405-93B939F3639B} = {22447274-717D-4321-87F3-868BAF93CBEC}
{53D0AA09-F5FA-4721-8C1B-375CBD15B4E8} = {5F0996AB-F8DB-4240-BD4A-DFDD70638A73}
{C6EE337B-91EA-472A-87C7-E9528408CE59} = {5F0996AB-F8DB-4240-BD4A-DFDD70638A73}
{F39D8F09-6233-4495-ACD0-F98904993B7E} = {55BE6759-95AA-434D-925D-A8D32F274E66}
{295284BA-D4E4-40AA-A2C2-BE36343F7DE6} = {55BE6759-95AA-434D-925D-A8D32F274E66}
{85DA00E5-CC11-463C-8577-C34967C328F7} = {1A2ABCD9-493B-4848-9C69-919CDBCA61F3}
{C1EBE17D-BFAD-47DA-88EB-BB073B84593E} = {1A2ABCD9-493B-4848-9C69-919CDBCA61F3}
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136} = {E2637D6D-04A5-4DE4-8AAF-E015C65DE8E1}
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C} = {E2637D6D-04A5-4DE4-8AAF-E015C65DE8E1}
{574222F8-9C26-4015-8F35-C1E5D41A505F} = {53D0AA09-F5FA-4721-8C1B-375CBD15B4E8}
{B8F734F5-873C-4367-9EBD-38EA420CD868} = {53D0AA09-F5FA-4721-8C1B-375CBD15B4E8}
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4} = {F39D8F09-6233-4495-ACD0-F98904993B7E}
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA} = {F39D8F09-6233-4495-ACD0-F98904993B7E}
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7} = {85DA00E5-CC11-463C-8577-C34967C328F7}
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837} = {85DA00E5-CC11-463C-8577-C34967C328F7}
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2} = {C6EE337B-91EA-472A-87C7-E9528408CE59}
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2} = {295284BA-D4E4-40AA-A2C2-BE36343F7DE6}
{539364C8-88B1-48A3-8406-D0B19FF30509} = {C1EBE17D-BFAD-47DA-88EB-BB073B84593E}
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC} = {5185D5C5-0EAD-49D5-B405-93B939F3639B}
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8} = {C6EE337B-91EA-472A-87C7-E9528408CE59}
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB} = {C6EE337B-91EA-472A-87C7-E9528408CE59}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Release|Any CPU.Build.0 = Release|Any CPU
{E42BB533-4144-4D78-BCCE-50BA00BCADBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E42BB533-4144-4D78-BCCE-50BA00BCADBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E42BB533-4144-4D78-BCCE-50BA00BCADBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E42BB533-4144-4D78-BCCE-50BA00BCADBE}.Release|Any CPU.Build.0 = Release|Any CPU
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}.Release|Any CPU.Build.0 = Release|Any CPU
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}.Release|Any CPU.Build.0 = Release|Any CPU
{574222F8-9C26-4015-8F35-C1E5D41A505F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{574222F8-9C26-4015-8F35-C1E5D41A505F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{574222F8-9C26-4015-8F35-C1E5D41A505F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{574222F8-9C26-4015-8F35-C1E5D41A505F}.Release|Any CPU.Build.0 = Release|Any CPU
{B8F734F5-873C-4367-9EBD-38EA420CD868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8F734F5-873C-4367-9EBD-38EA420CD868}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8F734F5-873C-4367-9EBD-38EA420CD868}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8F734F5-873C-4367-9EBD-38EA420CD868}.Release|Any CPU.Build.0 = Release|Any CPU
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}.Release|Any CPU.Build.0 = Release|Any CPU
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}.Release|Any CPU.Build.0 = Release|Any CPU
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}.Release|Any CPU.Build.0 = Release|Any CPU
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}.Release|Any CPU.Build.0 = Release|Any CPU
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}.Release|Any CPU.Build.0 = Release|Any CPU
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}.Release|Any CPU.Build.0 = Release|Any CPU
{539364C8-88B1-48A3-8406-D0B19FF30509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{539364C8-88B1-48A3-8406-D0B19FF30509}.Debug|Any CPU.Build.0 = Debug|Any CPU
{539364C8-88B1-48A3-8406-D0B19FF30509}.Release|Any CPU.ActiveCfg = Release|Any CPU
{539364C8-88B1-48A3-8406-D0B19FF30509}.Release|Any CPU.Build.0 = Release|Any CPU
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Release|Any CPU.Build.0 = Release|Any CPU
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Release|Any CPU.Build.0 = Release|Any CPU
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -1,348 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CD4A4407-C3B0-422D-BB8C-2A810CED9938}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiGateway", "ApiGateway", "{CDFA86FA-BBBA-4A5B-A833-3BE219E373E5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{C734CEF7-A2AC-3076-84D8-694B7490AA9D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Flight", "Flight", "{A3579DE0-F7C5-67E8-3CF8-3AC89B64E059}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Booking", "Booking", "{C6034A5C-F49A-5FA4-86A6-65B2CB19613F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Passenger", "Passenger", "{9D4F3958-FE6E-C048-E6F9-6F53D8AF03CA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{B465D535-05D9-3A0A-08BF-35A1C18CEC46}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A2834164-BF04-BF13-ADC5-A97145852861}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1B4FBE3A-43F5-1B1E-2877-3036AC5431EF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DDEDC5E0-5D13-A45C-2393-A774DD4A1A07}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{51D8F471-B8EB-AD1C-0E89-AA84C5D0C759}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{773BFBD8-04CD-79F8-8301-C81308C3ED45}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4B043475-1AFA-C467-FE09-A46D09CD6936}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CED3889-AECF-A6CD-55DC-F680D3C18861}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{54BCCDE8-25E6-6FCB-4A9E-D5D2AF76D352}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking.Api", "src\Services\Booking\src\Booking.Api\Booking.Api.csproj", "{D3BF565A-C413-4185-9528-BE1B4F46993C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking", "src\Services\Booking\src\Booking\Booking.csproj", "{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flight", "src\Services\Flight\src\Flight\Flight.csproj", "{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flight.Api", "src\Services\Flight\src\Flight.Api\Flight.Api.csproj", "{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity", "src\Services\Identity\src\Identity\Identity.csproj", "{BCDEAB10-6373-46E7-B408-846A3B0B508B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.Api", "src\Services\Identity\src\Identity.Api\Identity.Api.csproj", "{B0EC74C5-9B2D-492C-ABAE-3E868397B122}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Passenger", "src\Services\Passenger\src\Passenger\Passenger.csproj", "{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Passenger.Api", "src\Services\Passenger\src\Passenger.Api\Passenger.Api.csproj", "{101FFD12-17A4-4615-9438-F347BBF4CC85}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildingBlocks", "src\BuildingBlocks\BuildingBlocks.csproj", "{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{427BE8BE-DA7B-FC74-412B-547671E05463}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiGateway", "src\ApiGateway\src\ApiGateway.csproj", "{C015BF35-6977-407B-8948-636A9C81C5BE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Booking\tests\IntegrationTest\Integration.Test.csproj", "{19A89F36-FD3A-448D-90D1-04A1B67BB255}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EndToEnd.Test", "src\Services\Flight\tests\EndToEndTest\EndToEnd.Test.csproj", "{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Flight\tests\IntegrationTest\Integration.Test.csproj", "{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit.Test", "src\Services\Flight\tests\UnitTest\Unit.Test.csproj", "{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Identity\tests\IntegrationTest\Integration.Test.csproj", "{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Passenger\tests\IntegrationTest\Integration.Test.csproj", "{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aspire", "Aspire", "{D1B6353A-63F5-4DD9-90E6-42B2CFDF1DEA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C4287034-6833-4505-A6EB-704A86392ECB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppHost", "src\Aspire\src\AppHost\AppHost.csproj", "{490BCB11-314C-473C-9B85-A32164783507}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceDefaults", "src\Aspire\src\ServiceDefaults\ServiceDefaults.csproj", "{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|x64.ActiveCfg = Debug|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|x64.Build.0 = Debug|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|x86.ActiveCfg = Debug|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|x86.Build.0 = Debug|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|Any CPU.Build.0 = Release|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|x64.ActiveCfg = Release|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|x64.Build.0 = Release|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|x86.ActiveCfg = Release|Any CPU
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|x86.Build.0 = Release|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|x64.ActiveCfg = Debug|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|x64.Build.0 = Debug|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|x86.ActiveCfg = Debug|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|x86.Build.0 = Debug|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|Any CPU.Build.0 = Release|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|x64.ActiveCfg = Release|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|x64.Build.0 = Release|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|x86.ActiveCfg = Release|Any CPU
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|x86.Build.0 = Release|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|x64.ActiveCfg = Debug|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|x64.Build.0 = Debug|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|x86.ActiveCfg = Debug|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|x86.Build.0 = Debug|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|Any CPU.Build.0 = Release|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|x64.ActiveCfg = Release|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|x64.Build.0 = Release|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|x86.ActiveCfg = Release|Any CPU
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|x86.Build.0 = Release|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|x64.ActiveCfg = Debug|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|x64.Build.0 = Debug|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|x86.ActiveCfg = Debug|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|x86.Build.0 = Debug|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|Any CPU.Build.0 = Release|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|x64.ActiveCfg = Release|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|x64.Build.0 = Release|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|x86.ActiveCfg = Release|Any CPU
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|x86.Build.0 = Release|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|x64.ActiveCfg = Debug|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|x64.Build.0 = Debug|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|x86.ActiveCfg = Debug|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|x86.Build.0 = Debug|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|Any CPU.Build.0 = Release|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|x64.ActiveCfg = Release|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|x64.Build.0 = Release|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|x86.ActiveCfg = Release|Any CPU
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|x86.Build.0 = Release|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|x64.ActiveCfg = Debug|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|x64.Build.0 = Debug|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|x86.ActiveCfg = Debug|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|x86.Build.0 = Debug|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|Any CPU.Build.0 = Release|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|x64.ActiveCfg = Release|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|x64.Build.0 = Release|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|x86.ActiveCfg = Release|Any CPU
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|x86.Build.0 = Release|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|x64.ActiveCfg = Debug|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|x64.Build.0 = Debug|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|x86.ActiveCfg = Debug|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|x86.Build.0 = Debug|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|Any CPU.Build.0 = Release|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|x64.ActiveCfg = Release|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|x64.Build.0 = Release|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|x86.ActiveCfg = Release|Any CPU
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|x86.Build.0 = Release|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|Any CPU.Build.0 = Debug|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|x64.ActiveCfg = Debug|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|x64.Build.0 = Debug|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|x86.ActiveCfg = Debug|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|x86.Build.0 = Debug|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|Any CPU.ActiveCfg = Release|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|Any CPU.Build.0 = Release|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|x64.ActiveCfg = Release|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|x64.Build.0 = Release|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|x86.ActiveCfg = Release|Any CPU
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|x86.Build.0 = Release|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|x64.ActiveCfg = Debug|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|x64.Build.0 = Debug|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|x86.ActiveCfg = Debug|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|x86.Build.0 = Debug|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|Any CPU.Build.0 = Release|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|x64.ActiveCfg = Release|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|x64.Build.0 = Release|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|x86.ActiveCfg = Release|Any CPU
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|x86.Build.0 = Release|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|x64.ActiveCfg = Debug|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|x64.Build.0 = Debug|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|x86.ActiveCfg = Debug|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|x86.Build.0 = Debug|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|Any CPU.Build.0 = Release|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|x64.ActiveCfg = Release|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|x64.Build.0 = Release|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|x86.ActiveCfg = Release|Any CPU
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|x86.Build.0 = Release|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|x64.ActiveCfg = Debug|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|x64.Build.0 = Debug|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|x86.ActiveCfg = Debug|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|x86.Build.0 = Debug|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|Any CPU.Build.0 = Release|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|x64.ActiveCfg = Release|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|x64.Build.0 = Release|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|x86.ActiveCfg = Release|Any CPU
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|x86.Build.0 = Release|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|x64.ActiveCfg = Debug|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|x64.Build.0 = Debug|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|x86.ActiveCfg = Debug|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|x86.Build.0 = Debug|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|Any CPU.Build.0 = Release|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|x64.ActiveCfg = Release|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|x64.Build.0 = Release|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|x86.ActiveCfg = Release|Any CPU
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|x86.Build.0 = Release|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|x64.ActiveCfg = Debug|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|x64.Build.0 = Debug|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|x86.ActiveCfg = Debug|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|x86.Build.0 = Debug|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|Any CPU.Build.0 = Release|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|x64.ActiveCfg = Release|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|x64.Build.0 = Release|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|x86.ActiveCfg = Release|Any CPU
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|x86.Build.0 = Release|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|x64.ActiveCfg = Debug|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|x64.Build.0 = Debug|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|x86.ActiveCfg = Debug|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|x86.Build.0 = Debug|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|Any CPU.Build.0 = Release|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|x64.ActiveCfg = Release|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|x64.Build.0 = Release|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|x86.ActiveCfg = Release|Any CPU
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|x86.Build.0 = Release|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|x64.ActiveCfg = Debug|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|x64.Build.0 = Debug|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|x86.ActiveCfg = Debug|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|x86.Build.0 = Debug|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|Any CPU.Build.0 = Release|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|x64.ActiveCfg = Release|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|x64.Build.0 = Release|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|x86.ActiveCfg = Release|Any CPU
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|x86.Build.0 = Release|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|x64.ActiveCfg = Debug|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|x64.Build.0 = Debug|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|x86.ActiveCfg = Debug|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|x86.Build.0 = Debug|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|Any CPU.Build.0 = Release|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|x64.ActiveCfg = Release|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|x64.Build.0 = Release|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|x86.ActiveCfg = Release|Any CPU
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|x86.Build.0 = Release|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Debug|Any CPU.Build.0 = Debug|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Debug|x64.ActiveCfg = Debug|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Debug|x64.Build.0 = Debug|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Debug|x86.ActiveCfg = Debug|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Debug|x86.Build.0 = Debug|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Release|Any CPU.ActiveCfg = Release|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Release|Any CPU.Build.0 = Release|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Release|x64.ActiveCfg = Release|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Release|x64.Build.0 = Release|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Release|x86.ActiveCfg = Release|Any CPU
{490BCB11-314C-473C-9B85-A32164783507}.Release|x86.Build.0 = Release|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|x64.ActiveCfg = Debug|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|x64.Build.0 = Debug|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|x86.ActiveCfg = Debug|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|x86.Build.0 = Debug|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|Any CPU.Build.0 = Release|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|x64.ActiveCfg = Release|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|x64.Build.0 = Release|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|x86.ActiveCfg = Release|Any CPU
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{CDFA86FA-BBBA-4A5B-A833-3BE219E373E5} = {CD4A4407-C3B0-422D-BB8C-2A810CED9938}
{B19FD14B-4DFE-26B6-646B-3D5D94CC4D36} = {CD4A4407-C3B0-422D-BB8C-2A810CED9938}
{C734CEF7-A2AC-3076-84D8-694B7490AA9D} = {CD4A4407-C3B0-422D-BB8C-2A810CED9938}
{A3579DE0-F7C5-67E8-3CF8-3AC89B64E059} = {B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}
{C6034A5C-F49A-5FA4-86A6-65B2CB19613F} = {B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}
{9D4F3958-FE6E-C048-E6F9-6F53D8AF03CA} = {B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}
{B465D535-05D9-3A0A-08BF-35A1C18CEC46} = {B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}
{A2834164-BF04-BF13-ADC5-A97145852861} = {C6034A5C-F49A-5FA4-86A6-65B2CB19613F}
{1B4FBE3A-43F5-1B1E-2877-3036AC5431EF} = {C6034A5C-F49A-5FA4-86A6-65B2CB19613F}
{DDEDC5E0-5D13-A45C-2393-A774DD4A1A07} = {A3579DE0-F7C5-67E8-3CF8-3AC89B64E059}
{51D8F471-B8EB-AD1C-0E89-AA84C5D0C759} = {A3579DE0-F7C5-67E8-3CF8-3AC89B64E059}
{773BFBD8-04CD-79F8-8301-C81308C3ED45} = {B465D535-05D9-3A0A-08BF-35A1C18CEC46}
{4B043475-1AFA-C467-FE09-A46D09CD6936} = {B465D535-05D9-3A0A-08BF-35A1C18CEC46}
{5CED3889-AECF-A6CD-55DC-F680D3C18861} = {9D4F3958-FE6E-C048-E6F9-6F53D8AF03CA}
{54BCCDE8-25E6-6FCB-4A9E-D5D2AF76D352} = {9D4F3958-FE6E-C048-E6F9-6F53D8AF03CA}
{D3BF565A-C413-4185-9528-BE1B4F46993C} = {A2834164-BF04-BF13-ADC5-A97145852861}
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8} = {A2834164-BF04-BF13-ADC5-A97145852861}
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279} = {DDEDC5E0-5D13-A45C-2393-A774DD4A1A07}
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2} = {DDEDC5E0-5D13-A45C-2393-A774DD4A1A07}
{BCDEAB10-6373-46E7-B408-846A3B0B508B} = {773BFBD8-04CD-79F8-8301-C81308C3ED45}
{B0EC74C5-9B2D-492C-ABAE-3E868397B122} = {773BFBD8-04CD-79F8-8301-C81308C3ED45}
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538} = {5CED3889-AECF-A6CD-55DC-F680D3C18861}
{101FFD12-17A4-4615-9438-F347BBF4CC85} = {5CED3889-AECF-A6CD-55DC-F680D3C18861}
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2} = {C734CEF7-A2AC-3076-84D8-694B7490AA9D}
{427BE8BE-DA7B-FC74-412B-547671E05463} = {CDFA86FA-BBBA-4A5B-A833-3BE219E373E5}
{C015BF35-6977-407B-8948-636A9C81C5BE} = {427BE8BE-DA7B-FC74-412B-547671E05463}
{19A89F36-FD3A-448D-90D1-04A1B67BB255} = {1B4FBE3A-43F5-1B1E-2877-3036AC5431EF}
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4} = {51D8F471-B8EB-AD1C-0E89-AA84C5D0C759}
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B} = {51D8F471-B8EB-AD1C-0E89-AA84C5D0C759}
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB} = {51D8F471-B8EB-AD1C-0E89-AA84C5D0C759}
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01} = {4B043475-1AFA-C467-FE09-A46D09CD6936}
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5} = {54BCCDE8-25E6-6FCB-4A9E-D5D2AF76D352}
{D1B6353A-63F5-4DD9-90E6-42B2CFDF1DEA} = {CD4A4407-C3B0-422D-BB8C-2A810CED9938}
{C4287034-6833-4505-A6EB-704A86392ECB} = {D1B6353A-63F5-4DD9-90E6-42B2CFDF1DEA}
{490BCB11-314C-473C-9B85-A32164783507} = {C4287034-6833-4505-A6EB-704A86392ECB}
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4} = {C4287034-6833-4505-A6EB-704A86392ECB}
EndGlobalSection
EndGlobal

View File

@ -1,7 +1,7 @@
## uncommnet this line for use kubernetes ingress controller instead of Yarp
//@api-gateway=https://booking-microservices.com
@api-gateway=https://localhost:5000
//@api-gateway=https://booking-microservices.com
@api-gateway=https://localhost:5000
@identity-api=http://localhost:6005
@flight-api=http://localhost:5004
@passenger-api=http://localhost:6012
@ -29,9 +29,7 @@ grant_type=password
&client_secret=secret
&username=samh
&password=Admin@123456
&scope=flight-api role
### change scope base on microservices scope (eg. passenger-api, ...)
&scope=flight-api
###
@ -47,7 +45,7 @@ authorization: bearer {{Authenticate.response.body.access_token}}
"firstName": "John",
"lastName": "Do",
"username": "admin",
"passportNumber": "41290000",
"passportNumber": "412900000000",
"email": "admin@admin.com",
"password": "Admin@12345",
"confirmPassword": "Admin@12345"
@ -220,7 +218,7 @@ Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}}
{
"passportNumber": "41290000",
"passportNumber": "412900000000",
"passengerType": 1,
"age": 30
}

View File

@ -1,8 +0,0 @@
# Dashboards
- [Introducing ASP.NET Core metrics and Grafana dashboards in .NET 8](https://devblogs.microsoft.com/dotnet/introducing-aspnetcore-metrics-and-grafana-dashboards-in-dotnet-8/)
- [ASP.NET Core](https://grafana.com/grafana/dashboards/19924-asp-net-core/)
- [ASP.NET Core Endpoint](https://grafana.com/grafana/dashboards/19925-asp-net-core-endpoint/)
- [Node Exporter Quickstart and Dashboard](https://grafana.com/grafana/dashboards/13978-node-exporter-quickstart-and-dashboard/)
- [PostgreSQL Exporter Quickstart and Dashboard](https://grafana.com/grafana/dashboards/14114-postgres-overview/)
- [RabbitMQ-Overview](https://grafana.com/grafana/dashboards/10991-rabbitmq-overview/)

View File

@ -1,892 +0,0 @@
{
"__inputs": [],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "7.4.3"
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"version": "1.0.0"
}
],
"annotations": {
"list": []
},
"editable": false,
"gnetId": 13978,
"graphTooltip": 0,
"hideControls": false,
"id": null,
"links": [],
"refresh": "",
"rows": [
{
"collapse": false,
"collapsed": false,
"panels": [
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fill": 1,
"fillGradient": 0,
"gridPos": {},
"id": 2,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": null,
"seriesOverrides": [],
"spaceLength": 10,
"span": 6,
"stack": true,
"steppedLine": false,
"targets": [
{
"expr": "(\n (1 - rate(node_cpu_seconds_total{job=\"node\", mode=\"idle\", instance=\"$instance\"}[$__interval]))\n/ ignoring(cpu) group_left\n count without (cpu)( node_cpu_seconds_total{job=\"node\", mode=\"idle\", instance=\"$instance\"})\n)\n",
"format": "time_series",
"interval": "1m",
"intervalFactor": 5,
"legendFormat": "{{cpu}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "CPU Usage",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "percentunit",
"label": null,
"logBase": 1,
"max": 1,
"min": 0,
"show": true
},
{
"format": "percentunit",
"label": null,
"logBase": 1,
"max": 1,
"min": 0,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fill": 0,
"fillGradient": 0,
"gridPos": {},
"id": 3,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": null,
"seriesOverrides": [],
"spaceLength": 10,
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "node_load1{job=\"node\", instance=\"$instance\"}",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "1m load average",
"refId": "A"
},
{
"expr": "node_load5{job=\"node\", instance=\"$instance\"}",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "5m load average",
"refId": "B"
},
{
"expr": "node_load15{job=\"node\", instance=\"$instance\"}",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "15m load average",
"refId": "C"
},
{
"expr": "count(node_cpu_seconds_total{job=\"node\", instance=\"$instance\", mode=\"idle\"})",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "logical cores",
"refId": "D"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Load Average",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
}
]
}
],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": false,
"title": "Dashboard Row",
"titleSize": "h6",
"type": "row"
},
{
"collapse": false,
"collapsed": false,
"panels": [
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fill": 1,
"fillGradient": 0,
"gridPos": {},
"id": 4,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": null,
"seriesOverrides": [],
"spaceLength": 10,
"span": 9,
"stack": true,
"steppedLine": false,
"targets": [
{
"expr": "(\n node_memory_MemTotal_bytes{job=\"node\", instance=\"$instance\"}\n-\n node_memory_MemFree_bytes{job=\"node\", instance=\"$instance\"}\n-\n node_memory_Buffers_bytes{job=\"node\", instance=\"$instance\"}\n-\n node_memory_Cached_bytes{job=\"node\", instance=\"$instance\"}\n)\n",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "memory used",
"refId": "A"
},
{
"expr": "node_memory_Buffers_bytes{job=\"node\", instance=\"$instance\"}",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "memory buffers",
"refId": "B"
},
{
"expr": "node_memory_Cached_bytes{job=\"node\", instance=\"$instance\"}",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "memory cached",
"refId": "C"
},
{
"expr": "node_memory_MemFree_bytes{job=\"node\", instance=\"$instance\"}",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "memory free",
"refId": "D"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Memory Usage",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
}
]
},
{
"cacheTimeout": null,
"colorBackground": false,
"colorValue": false,
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"datasource": "$datasource",
"format": "percent",
"gauge": {
"maxValue": 100,
"minValue": 0,
"show": true,
"thresholdLabels": false,
"thresholdMarkers": true
},
"gridPos": {},
"id": 5,
"interval": null,
"links": [],
"mappingType": 1,
"mappingTypes": [
{
"name": "value to text",
"value": 1
},
{
"name": "range to text",
"value": 2
}
],
"maxDataPoints": 100,
"nullPointMode": "connected",
"nullText": null,
"postfix": "",
"postfixFontSize": "50%",
"prefix": "",
"prefixFontSize": "50%",
"rangeMaps": [
{
"from": "null",
"text": "N/A",
"to": "null"
}
],
"span": 3,
"sparkline": {
"fillColor": "rgba(31, 118, 189, 0.18)",
"full": false,
"lineColor": "rgb(31, 120, 193)",
"show": false
},
"tableColumn": "",
"targets": [
{
"expr": "100 -\n(\n avg(node_memory_MemAvailable_bytes{job=\"node\", instance=\"$instance\"})\n/\n avg(node_memory_MemTotal_bytes{job=\"node\", instance=\"$instance\"})\n* 100\n)\n",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "",
"refId": "A"
}
],
"thresholds": "80, 90",
"title": "Memory Usage",
"type": "singlestat",
"valueFontSize": "80%",
"valueMaps": [
{
"op": "=",
"text": "N/A",
"value": "null"
}
],
"valueName": "current"
}
],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": false,
"title": "Dashboard Row",
"titleSize": "h6",
"type": "row"
},
{
"collapse": false,
"collapsed": false,
"panels": [
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fill": 0,
"fillGradient": 0,
"gridPos": {},
"id": 6,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": null,
"seriesOverrides": [
{
"alias": "/ read| written/",
"yaxis": 1
},
{
"alias": "/ io time/",
"yaxis": 2
}
],
"spaceLength": 10,
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "rate(node_disk_read_bytes_total{job=\"node\", instance=\"$instance\", device!=\"\"}[$__interval])",
"format": "time_series",
"interval": "1m",
"intervalFactor": 2,
"legendFormat": "{{device}} read",
"refId": "A"
},
{
"expr": "rate(node_disk_written_bytes_total{job=\"node\", instance=\"$instance\", device!=\"\"}[$__interval])",
"format": "time_series",
"interval": "1m",
"intervalFactor": 2,
"legendFormat": "{{device}} written",
"refId": "B"
},
{
"expr": "rate(node_disk_io_time_seconds_total{job=\"node\", instance=\"$instance\", device!=\"\"}[$__interval])",
"format": "time_series",
"interval": "1m",
"intervalFactor": 2,
"legendFormat": "{{device}} io time",
"refId": "C"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Disk I/O",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "s",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fill": 1,
"fillGradient": 0,
"gridPos": {},
"id": 7,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": null,
"seriesOverrides": [
{
"alias": "used",
"color": "#E0B400"
},
{
"alias": "available",
"color": "#73BF69"
}
],
"spaceLength": 10,
"span": 6,
"stack": true,
"steppedLine": false,
"targets": [
{
"expr": "sum(\n max by (device) (\n node_filesystem_size_bytes{job=\"node\", instance=\"$instance\", fstype!=\"\"}\n -\n node_filesystem_avail_bytes{job=\"node\", instance=\"$instance\", fstype!=\"\"}\n )\n)\n",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "used",
"refId": "A"
},
{
"expr": "sum(\n max by (device) (\n node_filesystem_avail_bytes{job=\"node\", instance=\"$instance\", fstype!=\"\"}\n )\n)\n",
"format": "time_series",
"intervalFactor": 2,
"legendFormat": "available",
"refId": "B"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Disk Space Usage",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
}
]
}
],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": false,
"title": "Dashboard Row",
"titleSize": "h6",
"type": "row"
},
{
"collapse": false,
"collapsed": false,
"panels": [
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fill": 0,
"fillGradient": 0,
"gridPos": {},
"id": 8,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": null,
"seriesOverrides": [],
"spaceLength": 10,
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "rate(node_network_receive_bytes_total{job=\"node\", instance=\"$instance\", device!=\"lo\"}[$__interval])",
"format": "time_series",
"interval": "1m",
"intervalFactor": 2,
"legendFormat": "{{device}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Network Received",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fill": 0,
"fillGradient": 0,
"gridPos": {},
"id": 9,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"sideWidth": null,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": null,
"seriesOverrides": [],
"spaceLength": 10,
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "rate(node_network_transmit_bytes_total{job=\"node\", instance=\"$instance\", device!=\"lo\"}[$__interval])",
"format": "time_series",
"interval": "1m",
"intervalFactor": 2,
"legendFormat": "{{device}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "Network Transmitted",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": 0,
"show": true
}
]
}
],
"repeat": null,
"repeatIteration": null,
"repeatRowId": null,
"showTitle": false,
"title": "Dashboard Row",
"titleSize": "h6",
"type": "row"
}
],
"schemaVersion": 14,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"current": {
"text": "Prometheus",
"value": "Prometheus"
},
"hide": 0,
"label": null,
"name": "datasource",
"options": [],
"query": "prometheus",
"refresh": 1,
"regex": "",
"type": "datasource"
},
{
"allValue": null,
"current": {},
"datasource": "$datasource",
"hide": 0,
"includeAll": false,
"label": null,
"multi": false,
"name": "instance",
"options": [],
"query": "label_values(node_exporter_build_info{job=\"node\"}, instance)",
"refresh": 2,
"regex": "",
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "browser",
"title": "Node Exporter Quickstart and Dashboard",
"version": 0,
"description": "A quickstart to setup Prometheus Node Exporter with preconfigured dashboards, alerting rules, and recording rules."
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
# https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards
apiVersion: 1
providers:
- name: "default"
orgId: 1
folder: ""
type: file
disableDeletion: false
editable: true
allowUiUpdates: true
updateIntervalSeconds: 5 # how often Grafana will scan for changed dashboards
options:
path: /var/lib/grafana/dashboards # path to dashboards on disk

View File

@ -1,88 +0,0 @@
# https://grafana.com/docs/grafana/latest/administration/provisioning/
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/grafana-datasources.yaml
# https://github.com/grafana/intro-to-mltp/blob/main/grafana/provisioning/datasources/datasources.yaml
apiVersion: 1
datasources:
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/grafana-datasources.yaml
- name: Prometheus
type: prometheus
typeName: Prometheus
uid: prometheus-uid
access: proxy
orgId: 1
url: http://prometheus:9090
basicAuth: false
isDefault: true
readOnly: false
user: ''
database: ''
version: 1
editable: false
jsonData:
httpMethod: GET
- name: Jaeger
type: jaeger
access: proxy
url: http://jaeger-all-in-one:16686
editable: false
uid: jaeger-uid
- name: Zipkin
type: zipkin
access: proxy
url: http://zipkin-all-in-one:9411
editable: false
uid: zipkin-uid
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/grafana-datasources.yaml
- name: Tempo
type: tempo
access: proxy
orgId: 1
url: http://tempo:3200
basicAuth: false
isDefault: false
version: 1
editable: false
apiVersion: 1
uid: tempo-uid
jsonData:
httpMethod: GET
serviceMap:
datasourceUid: prometheus-uid
streamingEnabled:
search: true
#https://github.com/grafana/intro-to-mltp/blob/main/grafana/provisioning/datasources/datasources.yaml
- name: Loki
type: loki
access: proxy
uid: loki-uid
url: http://loki:3100
user: ''
database: ''
readOnly: false
jsonData:
derivedFields:
- datasourceUid: tempo-uid
matcherRegex: "^.*?traceI[d|D]=(\\w+).*$"
name: traceId
url: '$${__value.raw}'
- name: Kibana
type: elasticsearch
url: http://elasticsearch:9200
access: proxy
isDefault: false
uid: kibana-uid
jsonData:
esVersion: 7
timeField: "@timestamp"
maxConcurrentShardRequests: 256
interval: Daily
logMessageField: "message" # Optional: Field for log messages
logLevelField: "level" # Optional: Field for log levels
editable: true

View File

@ -1,44 +0,0 @@
# https://grafana.com/docs/loki/latest/configure/examples/configuration-examples/
# https://github.com/grafana/loki/issues/2018#issuecomment-970789233
# https://grafana.com/docs/opentelemetry/collector/send-logs-to-loki/
# https://github.com/grafana/loki/blob/main/examples/getting-started/loki-config.yaml
# https://github.com/grafana/loki/blob/main/cmd/loki/loki-local-config.yaml
# https://grafana.com/docs/loki/latest/configure/examples/configuration-examples/#1-local-configuration-exampleyaml
---
# https://grafana.com/docs/loki/latest/configure/examples/configuration-examples/#1-local-configuration-exampleyaml
auth_enabled: false
# This is a complete configuration to deploy Loki backed by the filesystem.
# The index will be shipped to the storage via tsdb-shipper.
server:
http_listen_port: 3100
common:
ring:
instance_addr: 127.0.0.1
kvstore:
store: inmemory
replication_factor: 1
path_prefix: /tmp/loki
schema_config:
configs:
- from: 2020-05-15
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
storage_config:
filesystem:
directory: /tmp/loki/chunks
# https://grafana.com/docs/loki/latest/send-data/otel/
# https://grafana.com/docs/loki/latest/send-data/otel/#changing-the-default-mapping-of-otlp-to-loki-format
limits_config:
# this attribute should be `true` when we use `otlphttp/loki`, but if we want to use `loki component` from `opentelemetry-collector-contrib` it should be false.
allow_structured_metadata: true

View File

@ -1,131 +0,0 @@
# ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/otel-collector-config.yaml
# https://opentelemetry.io/docs/collector/configuration/
# https://opentelemetry.io/docs/collector/architecture/
# https://betterstack.com/community/guides/observability/opentelemetry-collector/
# https://signoz.io/blog/opentelemetry-collector-complete-guide/
# This configuration sets up an OpenTelemetry Collector that receives trace data via the OTLP protocol over HTTP on port 4318, applies batch processing, and then exports the processed traces
# to exporter components like `Jaeger` endpoint located at `jaeger-all-in-one:4317`. It also includes a health_check extension for monitoring the collector's status.
# Receivers in the OpenTelemetry Collector are components that collect telemetry data (traces, metrics, and logs) from various sources, such as instrumented applications or agents.
# They act as entry points, converting incoming data into OpenTelemetry's internal format for processing and export.
# https://betterstack.com/community/guides/observability/opentelemetry-collector/#exploring-the-opentelemetry-collector-components
# https://opentelemetry.io/docs/collector/architecture/#receivers
# https://github.com/open-telemetry/opentelemetry-collector/blob/main/receiver/README.md
# https://opentelemetry.io/docs/collector/configuration/#receivers
receivers:
# supported receivers
# https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver
# instead of specifying details explicitly we can just use `otlp` and it uses both grpc and http
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
# prometheus:
# config:
# scrape_configs:
# - job_name: 'node-exporter'
# scrape_interval: 10s
# static_configs:
# - targets: [ 'node-exporter:9100' ]
# Processors in the OpenTelemetry Collector modify and enhance telemetry data by filtering, transforming, enriching, or batching it to prepare it for export.
# https://github.com/open-telemetry/opentelemetry-collector/tree/main/processor
processors:
batch: # Batches logs for better performance
# - Exporters in the OpenTelemetry Collector send processed telemetry data to backend systems like observability platforms, databases, or cloud services for storage, visualization, and analysis.
# - The `key` follows the `type/name` format, where `type` specifies the exporter `type` (e.g., otlp, kafka, prometheus), and `name` (optional) can be appended to provide a unique name for multiple instance of the same type
# https://betterstack.com/community/guides/observability/opentelemetry-collector/#exploring-the-opentelemetry-collector-components
# https://opentelemetry.io/docs/collector/architecture/#exporters
# https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter
# https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter
# https://opentelemetry.io/docs/collector/configuration/#exporters
exporters:
# valid values: [prometheusremotewrite zipkin otlphttp file kafka prometheus debug nop otlp opencensus]
# Prometheus exporter metrics
prometheus:
endpoint: "0.0.0.0:8889"
prometheusremotewrite:
endpoint: "http://prometheus:9090/api/v1/write"
# https://grafana.com/docs/loki/latest/send-data/otel/
# https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/lokiexporter/README.md
otlphttp/loki:
endpoint: "http://loki:3100/otlp"
tls:
insecure: true
# # we can also use `loki component` from `opentelemetry-collector-contrib` if we don't want to use builtin `otlphttp` exporter type and `http://loki:3100/otlp` loki endpoint
# loki:
# endpoint: "http://loki:3100/loki/api/v1/push"
# tls:
# insecure: true
debug:
# https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/elasticsearchexporter
# using `elasticsearch` from `opentelemetry-collector-contrib` components because it doesn't exist in `opentelemetry-collector`
elasticsearch:
endpoint: "http://elasticsearch:9200"
zipkin:
endpoint: "http://zipkin-all-in-one:9411/api/v2/spans"
format: proto
# export collected telemetry traces to jaeger OTLP grpc port, we can send data to other available endpoints and ports on jaeger as well
otlp/jaeger:
endpoint: "http://jaeger-all-in-one:4317"
tls:
insecure: true
otlp/tempo:
endpoint: "http://tempo:4317"
tls:
insecure: true
# seq-otlp:
# endpoint: "http://seq:5341/ingest/otlp"
# https://opentelemetry.io/docs/collector/configuration/#extensions
# https://github.com/open-telemetry/opentelemetry-collector/blob/main/extension/README.md
extensions:
pprof:
endpoint: 0.0.0.0:1888
zpages:
endpoint: 0.0.0.0:55679
health_check:
endpoint: 0.0.0.0:13133
# - The service section is used to configure what components are enabled in the Collector based on the configuration found in the receivers, processors, exporters, and extensions sections.
# - If a component is configured, but not defined within the service section, then its not enabled.
# https://betterstack.com/community/guides/observability/opentelemetry-collector/#exploring-the-opentelemetry-collector-components
# https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/README.md
# https://opentelemetry.io/docs/collector/architecture/
# https://opentelemetry.io/docs/collector/configuration/#service
service:
# The `service.extensions` subsection determines which of the configured extensions will be enabled
extensions: [pprof, zpages, health_check]
# The `service.pipeline` Each pipeline starts with one or more receivers collecting data, which is then processed sequentially by processors (applying transformations, filtering, or sampling).
# The processed data is finally sent to all configured exporters, ensuring each receives a copy. Components must be pre-configured in their respective sections before being used in a pipeline.
# pipeline activate predefined components, defined components are disabled by default
pipelines:
traces:
receivers: [otlp]
processors: [batch]
# https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#exporter-selection
exporters: [debug, zipkin, otlp/jaeger, otlp/tempo]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [debug, prometheusremotewrite, prometheus]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/loki, elasticsearch]

View File

@ -1,48 +0,0 @@
# ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/prometheus.yaml
# https://prometheus.io/docs/introduction/first_steps/
# https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
global:
scrape_interval: 5s
scrape_configs:
# when we use otel-collector we should comment other jobs in prometheus config, and we read configs from `otel-collector-config`
- job_name: "otel-collector"
scrape_interval: 10s
static_configs:
# otel-collector Prometheus exporter metrics
- targets: [ 'otel-collector:8889' ]
- targets: [ 'otel-collector:8888' ]
- job_name: "prometheus"
static_configs:
- targets: ["prometheus:9090"]
# # https://prometheus.io/docs/guides/node-exporter/
# # https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
# - job_name: "node-exporter"
# static_configs:
# - targets: [ 'node-exporter:9100' ]
# # if we don't use otel collector we should uncomment this
# # scrap application metrics
# # http://localhost:4000/metrics by AddPrometheusExporter()
# - job_name: vertical-slice-template-api
# scrape_interval: 10s
# metrics_path: /metrics
# static_configs:
# - targets: ['host.docker.internal:4000']
#
# # if we don't use otel collector we should uncomment this
# # scrap application health metrics
# # http://localhost:4000/health/metrics by AddPrometheusExporter()
# - job_name: vertical-slice-template-api-healthchecks
# scrape_interval: 10s
# metrics_path: /health/metrics
# static_configs:
# - targets: ['host.docker.internal:4000']
## https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/prometheus.yaml
# - job_name: 'tempo'
# static_configs:
# - targets: [ 'tempo:3200' ]

View File

@ -1,49 +0,0 @@
# https://grafana.com/docs/tempo/latest/configuration/
# https://github.com/grafana/tempo/blob/main/example/docker-compose/local/tempo.yaml
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/tempo.yaml
stream_over_http_enabled: true
server:
http_listen_port: 3200
log_level: info
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: "tempo:4317"
ingester:
max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally
compactor:
compaction:
block_retention: 1h # overall Tempo trace retention. set for demo purposes
metrics_generator:
registry:
external_labels:
source: tempo
cluster: docker-compose
storage:
path: /var/tempo/generator/wal
remote_write:
- url: http://prometheus:9090/api/v1/write
send_exemplars: true
traces_storage:
path: /var/tempo/generator/traces
storage:
trace:
backend: local # backend configuration to use
wal:
path: /var/tempo/wal # where to store the wal locally
local:
path: /var/tempo/blocks
overrides:
defaults:
metrics_generator:
processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator
generate_native_histograms: both

View File

@ -1,362 +0,0 @@
# ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/docker-compose.yaml
# ref: https://github.com/joaofbantunes/DotNetMicroservicesObservabilitySample/blob/main/docker-compose.yml
# ref: https://github.com/oskardudycz/EventSourcing.NetCore/blob/main/docker-compose.yml
# https://github.com/grafana/intro-to-mltp
# https://stackoverflow.com/questions/65272764/ports-are-not-available-listen-tcp-0-0-0-0-50070-bind-an-attempt-was-made-to
name: booking-microservices-infrastructure
services:
#######################################################
# rabbitmq
#######################################################
rabbitmq:
image: rabbitmq:management
container_name: rabbitmq
restart: unless-stopped
ports:
- "5672:5672"
- "15672:15672"
# volumes:
# - rabbitmq:/var/lib/rabbitmq
networks:
- infrastructure
#######################################################
# postgres
#######################################################
postgres:
image: postgres:latest
container_name: postgres
restart: unless-stopped
ports:
- '5432:5432'
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
command:
- "postgres"
- "-c"
- "wal_level=logical"
- "-c"
- "max_prepared_transactions=10"
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- infrastructure
#######################################################
# EventStoreDB
#######################################################
eventstore:
container_name: eventstore
image: eventstore/eventstore:latest
restart: unless-stopped
environment:
- EVENTSTORE_CLUSTER_SIZE=1
- EVENTSTORE_RUN_PROJECTIONS=All
- EVENTSTORE_START_STANDARD_PROJECTIONS=True
- EVENTSTORE_HTTP_PORT=2113
- EVENTSTORE_INSECURE=True
- EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=True
ports:
- "2113:2113"
networks:
- infrastructure
# #######################################################
# # Mongo
# #######################################################
mongo:
image: mongo:latest
container_name: mongo
restart: unless-stopped
# environment:
# - MONGO_INITDB_ROOT_USERNAME=root
# - MONGO_INITDB_ROOT_PASSWORD=secret
ports:
- 27017:27017
networks:
- infrastructure
# #######################################################
# # Redis
# #######################################################
redis:
image: redis
container_name: redis
restart: unless-stopped
ports:
- 6379:6379
networks:
- infrastructure
# #######################################################
# # jaeger
# # https://www.jaegertracing.io/docs/1.64/deployment/
# # https://www.jaegertracing.io/docs/1.6/deployment/
# #######################################################
jaeger-all-in-one:
image: jaegertracing/all-in-one:latest
container_name: jaeger-all-in-one
restart: unless-stopped
ports:
- "6831:6831/udp" # UDP port for Jaeger agent
- "16686:16686" # endpoints and Jaeger UI
- "14268:14268" # HTTP port for accept trace spans directly from clients
- "14317:4317" # OTLP gRPC receiver for jaeger
- "14318:4318" # OTLP http receiver for jaeger
# - "9411" # Accepts Zipkin spans - /api/v2/spans
networks:
- infrastructure
# #######################################################
# # zipkin
# # https://zipkin.io/pages/quickstart
# #######################################################
zipkin-all-in-one:
image: openzipkin/zipkin:latest
container_name: zipkin-all-in-one
restart: unless-stopped
ports:
- "9411:9411"
networks:
- infrastructure
# #######################################################
# # otel-collector
# # https://opentelemetry.io/docs/collector/installation/
# # https://github.com/open-telemetry/opentelemetry-collector
# # https://github.com/open-telemetry/opentelemetry-collector-contrib
# # we can use none contrib docker `otel/opentelemetry-collector` version from `https://github.com/open-telemetry/opentelemetry-collector` repository but,
# # if we need more components like `elasticsearch` we should use `otel/opentelemetry-collector-contrib` image of `https://github.com/open-telemetry/opentelemetry-collector-contrib` repository.
# #######################################################
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
container_name: otel-collector
restart: unless-stopped
command: ["--config=/etc/otelcol-contrib/config.yaml"]
volumes:
- ./../configs/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
ports:
- "11888:1888" # pprof extension
- "8888:8888" # Prometheus metrics exposed by the Collector
- "8889:8889" # Prometheus exporter metrics
- "13133:13133" # health_check extension
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP http receiver
- "55679:55679" # zpages extension
networks:
- infrastructure
# #######################################################
# # prometheus
# # https://prometheus.io/docs/introduction/first_steps/
# # https://prometheus.io/docs/prometheus/3.1/installation/
# # https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
# #######################################################
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./../configs/prometheus.yaml:/etc/prometheus/prometheus.yml
# to passe one flag, such as "--log.level=debug" or "--web.enable-remote-write-receiver", we need to override the whole command, as we can't just pass one extra argument
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
- "--web.console.templates=/usr/share/prometheus/consoles"
# need this for the OpenTelemetry collector to be able to put metrics into Prometheus
- "--web.enable-remote-write-receiver"
# - "--log.level=debug"
networks:
- infrastructure
# #######################################################
# # node-exporter
# # https://prometheus.io/docs/guides/node-exporter/
# # https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
# #######################################################
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
restart: unless-stopped
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
ports:
- "9101:9100"
networks:
- infrastructure
# #######################################################
# # grafana
# # https://grafana.com/docs/grafana/latest/administration/provisioning/
# # https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/
# # https://grafana.com/docs/grafana/latest/setup-grafana/configure-docker/
# # https://github.com/grafana/intro-to-mltp/blob/main/grafana/provisioning/datasources/datasources.yaml
# #######################################################
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
environment:
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
# - GF_AUTH_ANONYMOUS_ENABLED=true
# - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
# - GF_AUTH_DISABLE_LOGIN_FORM=true
depends_on:
- prometheus
ports:
- "3000:3000"
volumes:
- ./../configs/grafana/provisioning:/etc/grafana/provisioning
- ./../configs/grafana/dashboards:/var/lib/grafana/dashboards
## https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/
# - ./../configs/grafana/grafana.ini:/etc/grafana/grafana.ini
networks:
- infrastructure
# #######################################################
# # tempo
# # https://github.com/grafana/tempo/blob/main/example/docker-compose/otel-collector/docker-compose.yaml
# # https://github.com/grafana/tempo/blob/main/example/docker-compose/shared
# # https://github.com/grafana/tempo/blob/main/example/docker-compose/local
# # https://github.com/grafana/tempo/tree/main/example/docker-compose
# #######################################################
tempo:
image: grafana/tempo:latest
container_name: tempo
restart: unless-stopped
command: [ "-config.file=/etc/tempo.yaml" ]
volumes:
- ./../configs/tempo.yaml:/etc/tempo.yaml
ports:
- "3200" # tempo
- "24317:4317" # otlp grpc
- "24318:4318" # otlp http
networks:
- infrastructure
# #######################################################
# # loki
# # https://grafana.com/docs/opentelemetry/collector/send-logs-to-loki/
# # https://github.com/grafana/loki/blob/main/production/docker-compose.yaml
# # https://github.com/grafana/loki/blob/main/examples/getting-started/docker-compose.yaml
# #######################################################
loki:
image: grafana/loki:latest
hostname: loki
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./../configs/loki-config.yaml:/etc/loki/local-config.yaml
networks:
- infrastructure
# #######################################################
# # elasticsearch
# # https://www.elastic.co/guide/en/elasticsearch/reference/7.17/docker.html#docker-compose-file
# #######################################################
elasticsearch:
container_name: elasticsearch
restart: unless-stopped
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
environment:
- discovery.type=single-node
- cluster.name=docker-cluster
- node.name=docker-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- xpack.security.enabled=false
- xpack.security.http.ssl.enabled=false
- xpack.security.transport.ssl.enabled=false
- network.host=0.0.0.0
- http.port=9200
- transport.host=localhost
- bootstrap.memory_lock=true
- cluster.routing.allocation.disk.threshold_enabled=false
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- elastic-data:/usr/share/elasticsearch/data
ports:
- ${ELASTIC_HOST_PORT:-9200}:${ELASTIC_PORT:-9200}
- 9300:9300
networks:
- infrastructure
# #######################################################
# # kibana
# # https://www.elastic.co/guide/en/kibana/current/docker.html
# #######################################################
kibana:
image: docker.elastic.co/kibana/kibana:8.17.0
container_name: kibana
restart: unless-stopped
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- ${KIBANA_HOST_PORT:-5601}:${KIBANA_PORT:-5601}
depends_on:
- elasticsearch
networks:
- infrastructure
# #######################################################
# # cadvisor
# #######################################################
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
devices:
- /dev/kmsg
networks:
- infrastructure
networks:
infrastructure:
name: infrastructure
driver: bridge
volumes:
elastic-data:
postgres-data:

View File

@ -1,49 +1,79 @@
name: booking-microservices
version: "3.3"
services:
#######################################################
# rabbitmq
#######################################################
rabbitmq:
image: rabbitmq:management
container_name: rabbitmq
restart: unless-stopped
ports:
- "5672:5672"
- "15672:15672"
# volumes:
# - rabbitmq:/var/lib/rabbitmq
networks:
- booking
#######################################################
# postgres
#######################################################
#######################################################
# Postgres
######################################################
postgres:
image: postgres:latest
container_name: postgres
image: postgres:latest
container_name: postgres
restart: unless-stopped
ports:
- '5432:5432'
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
command:
- "postgres"
- "-c"
- "wal_level=logical"
- "-c"
- "max_prepared_transactions=10"
networks:
- booking
#######################################################
# SqlServer
#######################################################
# sql:
# container_name: sql
# image: mcr.microsoft.com/mssql/server
# restart: unless-stopped
# ports:
# - "1433:1433"
# environment:
# SA_PASSWORD: "Password@1234"
# ACCEPT_EULA: "Y"
#######################################################
# Rabbitmq
#######################################################
rabbitmq:
container_name: rabbitmq
image: rabbitmq:management
restart: unless-stopped
ports:
- '5432:5432'
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
command:
- "postgres"
- "-c"
- "wal_level=logical"
- "-c"
- "max_prepared_transactions=10"
volumes:
- postgres-data:/var/lib/postgresql/data
- 5672:5672
- 15672:15672
networks:
- booking
#######################################################
# EventStoreDB
#######################################################
#######################################################
# Jaeger
#######################################################
jaeger:
image: jaegertracing/all-in-one
container_name: jaeger
restart: unless-stopped
networks:
- booking
ports:
- 5775:5775/udp
- 5778:5778
- 6831:6831/udp
- 6832:6832/udp
- 9411:9411
- 14268:14268
- 16686:16686
#######################################################
# EventStoreDB
#######################################################
eventstore:
container_name: eventstore
image: eventstore/eventstore:latest
@ -61,9 +91,9 @@ services:
- booking
#######################################################
# Mongo
#######################################################
#######################################################
# Mongo
#######################################################
mongo:
image: mongo:latest
container_name: mongo
@ -71,260 +101,93 @@ services:
# environment:
# - MONGO_INITDB_ROOT_USERNAME=root
# - MONGO_INITDB_ROOT_PASSWORD=secret
networks:
- booking
ports:
- 27017:27017
networks:
- booking
#######################################################
# Redis
#######################################################
redis:
image: redis
container_name: redis
#######################################################
# Elastic Search
#######################################################
elasticsearch:
container_name: elasticsearch
image: elasticsearch:7.17.9
restart: unless-stopped
ports:
- 6379:6379
networks:
- booking
#######################################################
# jaeger
# https://www.jaegertracing.io/docs/1.64/deployment/
# https://www.jaegertracing.io/docs/1.6/deployment/
#######################################################
jaeger-all-in-one:
image: jaegertracing/all-in-one:latest
container_name: jaeger-all-in-one
restart: unless-stopped
ports:
- "6831:6831/udp" # UDP port for Jaeger agent
- "16686:16686" # endpoints and Jaeger UI
- "14268:14268" # HTTP port for accept trace spans directly from clients
- "14317:4317" # OTLP gRPC receiver for jaeger
- "14318:4318" # OTLP http receiver for jaeger
# - "9411" # Accepts Zipkin spans - /api/v2/spans
networks:
- booking
#######################################################
# zipkin
# https://zipkin.io/pages/quickstart
#######################################################
zipkin-all-in-one:
image: openzipkin/zipkin:latest
container_name: zipkin-all-in-one
restart: unless-stopped
ports:
- "9411:9411"
networks:
- booking
#######################################################
# otel-collector
# https://opentelemetry.io/docs/collector/installation/
# https://github.com/open-telemetry/opentelemetry-collector
# https://github.com/open-telemetry/opentelemetry-collector-contrib
# we can use none contrib docker `otel/opentelemetry-collector` version from `https://github.com/open-telemetry/opentelemetry-collector` repository but,
# if we need more components like `elasticsearch` we should use `otel/opentelemetry-collector-contrib` image of `https://github.com/open-telemetry/opentelemetry-collector-contrib` repository.
#######################################################
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
container_name: otel-collector
restart: unless-stopped
command: ["--config=/etc/otelcol-contrib/config.yaml"]
- 9200:9200
volumes:
- ./../configs/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
ports:
- "11888:1888" # pprof extension
- "8888:8888" # Prometheus metrics exposed by the Collector
- "8889:8889" # Prometheus exporter metrics
- "13133:13133" # health_check extension
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP http receiver
- "55679:55679" # zpages extension
- elasticsearch-data:/usr/share/elasticsearch/data
environment:
- xpack.monitoring.enabled=true
- xpack.watcher.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- discovery.type=single-node
networks:
- booking
#######################################################
# Kibana
#######################################################
kibana:
container_name: kibana
image: kibana:7.17.9
restart: unless-stopped
ports:
- 5601:5601
depends_on:
- elasticsearch
environment:
- ELASTICSEARCH_URL=elasticsearch:9200
networks:
- booking
#######################################################
# prometheus
# https://prometheus.io/docs/introduction/first_steps/
# https://prometheus.io/docs/prometheus/3.1/installation/
# https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
#######################################################
#######################################################
# prometheus
#######################################################
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
environment:
- TZ=UTC
volumes:
- ./../configs/prometheus.yaml:/etc/prometheus/prometheus.yml
# to passe one flag, such as "--log.level=debug" or "--web.enable-remote-write-receiver", we need to override the whole command, as we can't just pass one extra argument
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
- "--web.console.templates=/usr/share/prometheus/consoles"
# need this for the OpenTelemetry collector to be able to put metrics into Prometheus
- "--web.enable-remote-write-receiver"
# - "--log.level=debug"
- ./monitoring/prom/prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- booking
#######################################################
# node-exporter
# https://prometheus.io/docs/guides/node-exporter/
# https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
#######################################################
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
#######################################################
# grafana
#######################################################
grafana:
image: grafana/grafana
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./monitoring/grafana-data/data:/var/lib/grafana
networks:
- booking
#######################################################
# node_exporter
#######################################################
node_exporter:
image: quay.io/prometheus/node-exporter:latest
container_name: node_exporter
restart: unless-stopped
command:
- '--path.rootfs=/host'
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
ports:
- "9101:9100"
networks:
- booking
#######################################################
# grafana
# https://grafana.com/docs/grafana/latest/administration/provisioning/
# https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/
# https://grafana.com/docs/grafana/latest/setup-grafana/configure-docker/
# https://github.com/grafana/intro-to-mltp/blob/main/grafana/provisioning/datasources/datasources.yaml
#######################################################
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
environment:
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
# - GF_AUTH_ANONYMOUS_ENABLED=true
# - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
# - GF_AUTH_DISABLE_LOGIN_FORM=true
depends_on:
- prometheus
ports:
- "3000:3000"
volumes:
- ./../configs/grafana/provisioning:/etc/grafana/provisioning
- ./../configs/grafana/dashboards:/var/lib/grafana/dashboards
## https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/
# - ./../configs/grafana/grafana.ini:/etc/grafana/grafana.ini
networks:
- booking
#######################################################
# tempo
# https://github.com/grafana/tempo/blob/main/example/docker-compose/otel-collector/docker-compose.yaml
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared
# https://github.com/grafana/tempo/blob/main/example/docker-compose/local
# https://github.com/grafana/tempo/tree/main/example/docker-compose
#######################################################
tempo:
image: grafana/tempo:latest
container_name: tempo
restart: unless-stopped
command: [ "-config.file=/etc/tempo.yaml" ]
volumes:
- ./../configs/tempo.yaml:/etc/tempo.yaml
ports:
- "3200" # tempo
- "24317:4317" # otlp grpc
- "24318:4318" # otlp http
networks:
- booking
#######################################################
# loki
# https://grafana.com/docs/opentelemetry/collector/send-logs-to-loki/
# https://github.com/grafana/loki/blob/main/production/docker-compose.yaml
# https://github.com/grafana/loki/blob/main/examples/getting-started/docker-compose.yaml
#######################################################
loki:
image: grafana/loki:latest
hostname: loki
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./../configs/loki-config.yaml:/etc/loki/local-config.yaml
networks:
- booking
#######################################################
# elasticsearch
# https://www.elastic.co/guide/en/elasticsearch/reference/7.17/docker.html#docker-compose-file
#######################################################
elasticsearch:
container_name: elasticsearch
restart: unless-stopped
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
environment:
- discovery.type=single-node
- cluster.name=docker-cluster
- node.name=docker-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- xpack.security.enabled=false
- xpack.security.http.ssl.enabled=false
- xpack.security.transport.ssl.enabled=false
- network.host=0.0.0.0
- http.port=9200
- transport.host=localhost
- bootstrap.memory_lock=true
- cluster.routing.allocation.disk.threshold_enabled=false
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- elastic-data:/usr/share/elasticsearch/data
ports:
- ${ELASTIC_HOST_PORT:-9200}:${ELASTIC_PORT:-9200}
- 9300:9300
networks:
- booking
#######################################################
# kibana
# https://www.elastic.co/guide/en/kibana/current/docker.html
#######################################################
kibana:
image: docker.elastic.co/kibana/kibana:8.17.0
container_name: kibana
restart: unless-stopped
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- ${KIBANA_HOST_PORT:-5601}:${KIBANA_PORT:-5601}
depends_on:
- elasticsearch
networks:
- booking
#######################################################
# cadvisor
#######################################################
@ -345,18 +208,17 @@ services:
networks:
- booking
######################################################
# Api-Gateway
# Gateway
######################################################
api-gateway:
image: api-gateway
gateway:
image: gateway
build:
args:
Version: "1"
context: ../../
dockerfile: src/ApiGateway/Dockerfile
container_name: api-gateway
dockerfile: src/ApiGateway/dev.Dockerfile
container_name: gateway
ports:
- "5001:80"
- "5000:443"
@ -382,7 +244,7 @@ services:
args:
Version: "1"
context: ../../
dockerfile: src/Services/Flight/Dockerfile
dockerfile: src/Services/Flight/dev.Dockerfile
container_name: flight
ports:
- 5004:80
@ -408,7 +270,7 @@ services:
args:
Version: "1"
context: ../../
dockerfile: src/Services/Identity/Dockerfile
dockerfile: src/Services/Identity/dev.Dockerfile
container_name: identity
ports:
- 6005:80
@ -435,7 +297,7 @@ services:
args:
Version: "1"
context: ../../
dockerfile: src/Services/Passenger/Dockerfile
dockerfile: src/Services/Passenger/dev.Dockerfile
container_name: passenger
ports:
- 6012:80
@ -462,7 +324,7 @@ services:
args:
Version: "1"
context: ../../
dockerfile: src/Services/Booking/Dockerfile
dockerfile: src/Services/Booking/dev.Dockerfile
container_name: booking
ports:
- 6010:80
@ -482,11 +344,9 @@ services:
networks:
booking:
name: booking
driver: bridge
volumes:
elastic-data:
postgres-data:
elasticsearch-data:

View File

@ -0,0 +1,234 @@
version: "3.3"
services:
#######################################################
# Rabbitmq
#######################################################
rabbitmq:
container_name: rabbitmq
image: rabbitmq:management
restart: unless-stopped
ports:
- 5672:5672
- 15672:15672
networks:
- booking
#######################################################
# Postgres
######################################################
postgres:
image: postgres:latest
container_name: postgres
restart: unless-stopped
ports:
- '5432:5432'
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
command:
- "postgres"
- "-c"
- "wal_level=logical"
- "-c"
- "max_prepared_transactions=10"
networks:
- booking
#######################################################
# SqlServer
#######################################################
# sql:
# container_name: sql
# image: mcr.microsoft.com/mssql/server
# restart: unless-stopped
# ports:
# - "1433:1433"
# environment:
# SA_PASSWORD: "Password@1234"
# ACCEPT_EULA: "Y"
#######################################################
# Jaeger
#######################################################
jaeger:
container_name: jaeger
image: jaegertracing/all-in-one
restart: unless-stopped
networks:
- booking
ports:
- 5775:5775/udp
- 5778:5778
- 6831:6831/udp
- 6832:6832/udp
- 9411:9411
- 14268:14268
- 16686:16686
# #######################################################
# # EventStoreDB
# #######################################################
# #https://stackoverflow.com/questions/65272764/ports-are-not-available-listen-tcp-0-0-0-0-50070-bind-an-attempt-was-made-to
eventstore:
container_name: eventstore
image: eventstore/eventstore:latest
restart: unless-stopped
environment:
- EVENTSTORE_CLUSTER_SIZE=1
- EVENTSTORE_RUN_PROJECTIONS=All
- EVENTSTORE_START_STANDARD_PROJECTIONS=True
- EVENTSTORE_HTTP_PORT=2113
- EVENTSTORE_INSECURE=True
- EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=True
ports:
- "2113:2113"
networks:
- booking
#######################################################
# Mongo
#######################################################
mongo:
image: mongo:latest
container_name: mongo
restart: unless-stopped
# environment:
# - MONGO_INITDB_ROOT_USERNAME=root
# - MONGO_INITDB_ROOT_PASSWORD=secret
networks:
- booking
ports:
- 27017:27017
#######################################################
# Elastic Search
#######################################################
elasticsearch:
container_name: elasticsearch
image: elasticsearch:7.17.9
restart: unless-stopped
ports:
- 9200:9200
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
environment:
- xpack.monitoring.enabled=true
- xpack.watcher.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- discovery.type=single-node
networks:
- booking
#######################################################
# Kibana
#######################################################
kibana:
container_name: kibana
image: kibana:7.17.9
restart: unless-stopped
ports:
- 5601:5601
depends_on:
- elasticsearch
environment:
- ELASTICSEARCH_URL=http://localhost:9200
networks:
- booking
#######################################################
# Redis
#######################################################
redis:
image: redis
container_name: redis
restart: unless-stopped
networks:
- booking
ports:
- 6379:6379
#######################################################
# prometheus
#######################################################
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
environment:
- TZ=UTC
volumes:
- ./monitoring/prom/prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- booking
#######################################################
# grafana
#######################################################
grafana:
image: grafana/grafana
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./monitoring/grafana-data/data:/var/lib/grafana
networks:
- booking
#######################################################
# node_exporter
#######################################################
node_exporter:
image: quay.io/prometheus/node-exporter:latest
container_name: node_exporter
restart: unless-stopped
command:
- '--path.rootfs=/host'
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
networks:
- booking
#######################################################
# cadvisor
#######################################################
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
devices:
- /dev/kmsg
networks:
- booking
networks:
booking:
volumes:
elasticsearch-data:

View File

@ -1304,4 +1304,4 @@
"uid": "KdDACDp4z",
"version": 5,
"weekStart": ""
}
}

View File

@ -0,0 +1,69 @@
global:
scrape_interval: 15s
scrape_timeout: 10s
evaluation_interval: 15s
alerting:
alertmanagers:
- scheme: http
timeout: 10s
api_version: v1
static_configs:
- targets: []
scrape_configs:
- job_name: prometheus
honor_timestamps: true
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets:
- localhost:9090
- job_name: cadvisor
static_configs:
- targets:
- cadvisor:8080
- job_name: node_exporter
static_configs:
- targets:
- node_exporter:9100
- job_name: flight
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets:
- host.docker.internal:5004
- job_name: identity
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets:
- host.docker.internal:6005
- job_name: passenger
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets:
- host.docker.internal:6012
- job_name: booking
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets:
- host.docker.internal:6010
- job_name: gateway
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: https
static_configs:
- targets:
- host.docker.internal:5000

View File

@ -13,4 +13,4 @@ spec:
solvers:
- http01:
ingress:
class: nginx
class: nginx

File diff suppressed because it is too large Load Diff

45
deployments/tye/tye.yml Normal file
View File

@ -0,0 +1,45 @@
name: Booking
services:
- name: booking-gateway
project: ./../../src/ApiGateway/src/ApiGateway.csproj
bindings:
- port: 5001
env:
- name: ASPNETCORE_ENVIRONMENT
value: development
- name: flight
project: ./../../src/Services/Flight/src/Flight.Api/Flight.Api.csproj
bindings:
- port: 5003
env:
- name: ASPNETCORE_ENVIRONMENT
value: development
- name: identity
project: ./../../src/Services/Identity/src/Identity.Api/Identity.Api.csproj
bindings:
- port: 5005
env:
- name: ASPNETCORE_ENVIRONMENT
value: development
- name: passenger
project: ./../../src/Services/Passenger/src/Passenger.Api/Passenger.Api.csproj
bindings:
- port: 5012
env:
- name: ASPNETCORE_ENVIRONMENT
value: development
- name: booking
project: ./../../src/Services/Booking/src/Booking.Api/Booking.Api.csproj
bindings:
- port: 5010
env:
- name: ASPNETCORE_ENVIRONMENT
value: development

View File

@ -1,6 +1,6 @@
{
"sdk": {
"version": "10.0.103",
"version": "8.0.401",
"rollForward": "latestFeature"
}
}

4
package-lock.json generated
View File

@ -1,11 +1,11 @@
{
"name": "monolith-to-cloud-architecture",
"name": "booking-microservices",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "monolith-to-cloud-architecture",
"name": "booking-microservices",
"version": "1.0.0",
"license": "MIT",
"devDependencies": {

View File

@ -7,8 +7,8 @@
"main": "index.js",
"scripts": {
"prepare": "husky && dotnet tool restore",
"format": "dotnet tool run dotnet-csharpier booking-microservices.sln",
"ci-format": "dotnet tool run dotnet-csharpier booking-microservices.sln --check",
"format": "dotnet format booking-microservices-sample.sln --severity error --verbosity detailed",
"ci-format": "dotnet format booking-microservices-sample.sln --verify-no-changes --severity error --verbosity detailed",
"upgrade-packages": "dotnet outdated --upgrade"
},
"devDependencies": {

View File

@ -1,38 +1,45 @@
# ---------- Build Stage ----------
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
WORKDIR /
# Copy solution-level files
COPY .editorconfig .
COPY global.json .
COPY Directory.Build.props .
COPY ./.editorconfig ./
COPY ./global.json ./
COPY ./Directory.Build.props ./
# Copy project files first (better Docker caching)
COPY src/BuildingBlocks/BuildingBlocks.csproj src/BuildingBlocks/
COPY src/ApiGateway/src/ApiGateway.csproj src/ApiGateway/src/
COPY src/Aspire/src/ServiceDefaults/ServiceDefaults.csproj src/Aspire/src/ServiceDefaults/
# Setup working directory for the project
COPY ./src/BuildingBlocks/BuildingBlocks.csproj ./BuildingBlocks/
COPY ./src/ApiGateway/src/ApiGateway.csproj ./ApiGateway/src/
# Restore dependencies
RUN dotnet restore src/ApiGateway/src/ApiGateway.csproj
# Copy the rest of the source code
COPY src ./src
# Restore nuget packages
RUN dotnet restore ./ApiGateway/src/ApiGateway.csproj
# Publish (build included)
RUN dotnet publish src/ApiGateway/src/ApiGateway.csproj \
-c Release \
-o /app/publish \
--no-restore
# Copy project files
COPY ./src/BuildingBlocks ./BuildingBlocks/
COPY ./src/ApiGateway/src ./ApiGateway/src/
# ---------- Runtime Stage ----------
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
# Build project with Release configuration
# and no restore, as we did it already
COPY --from=build /app/publish .
RUN ls
RUN dotnet build -c Release --no-restore ./ApiGateway/src/ApiGateway.csproj
ENV ASPNETCORE_URLS=http://+:80
ENV ASPNETCORE_ENVIRONMENT=docker
WORKDIR /ApiGateway/src
# Publish project to output folder
# and no build, as we did it already
RUN dotnet publish -c Release --no-build -o out
FROM mcr.microsoft.com/dotnet/aspnet:8.0
# Setup working directory for the project
WORKDIR /
COPY --from=builder /src/ApiGateway/src/out .
ENV ASPNETCORE_URLS https://*:443, http://*:80
ENV ASPNETCORE_ENVIRONMENT docker
EXPOSE 80
EXPOSE 443
ENTRYPOINT ["dotnet", "ApiGateway.dll"]
ENTRYPOINT ["dotnet", "ApiGateway.dll"]

View File

@ -0,0 +1,48 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
WORKDIR /
COPY ./.editorconfig ./
COPY ./global.json ./
COPY ./Directory.Build.props ./
# Setup working directory for the project
COPY ./src/BuildingBlocks/BuildingBlocks.csproj ./BuildingBlocks/
COPY ./src/ApiGateway/src/ApiGateway.csproj ./ApiGateway/src/
# Restore nuget packages
RUN --mount=type=cache,id=gateway_nuget,target=/root/.nuget/packages \
dotnet restore ./ApiGateway/src/ApiGateway.csproj
# Copy project files
COPY ./src/BuildingBlocks ./BuildingBlocks/
COPY ./src/ApiGateway/src ./ApiGateway/src/
# Build project with Release configuration
# and no restore, as we did it already
RUN ls
RUN --mount=type=cache,id=gateway_nuget,target=/root/.nuget/packages \
dotnet build -c Release --no-restore ./ApiGateway/src/ApiGateway.csproj
WORKDIR /ApiGateway/src
# Publish project to output folder
# and no build, as we did it already
RUN --mount=type=cache,id=gateway_nuget,target=/root/.nuget/packages \
dotnet publish -c Release --no-build -o out
FROM mcr.microsoft.com/dotnet/aspnet:8.0
# Setup working directory for the project
WORKDIR /
COPY --from=builder /src/ApiGateway/src/out .
ENV ASPNETCORE_URLS https://*:443, http://*:80
ENV ASPNETCORE_ENVIRONMENT docker
EXPOSE 80
EXPOSE 443
ENTRYPOINT ["dotnet", "ApiGateway.dll"]

View File

@ -1,5 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<ItemGroup>
<ProjectReference Include="..\..\BuildingBlocks\BuildingBlocks.csproj" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,7 @@
using BuildingBlocks.Logging;
using BuildingBlocks.Web;
using Figgle;
using Figgle.Fonts;
using Serilog;
var builder = WebApplication.CreateBuilder(args);
var env = builder.Environment;
@ -8,6 +9,8 @@ var appOptions = builder.Services.GetOptions<AppOptions>("AppOptions");
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
builder.AddCustomSerilog(env);
builder.Services.AddControllers();
builder.Services.AddHttpContextAccessor();
@ -15,6 +18,7 @@ builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSecti
var app = builder.Build();
app.UseSerilogRequestLogging();
app.UseCorrelationId();
app.UseRouting();
app.UseHttpsRedirection();
@ -29,4 +33,4 @@ app.UseEndpoints(endpoints =>
app.MapGet("/", x => x.Response.WriteAsync(appOptions.Name));
app.Run();
app.Run();

View File

@ -1,5 +1,13 @@
{
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:17191",
"sslPort": 44352
}
},
"profiles": {
"ApiGateway": {
"commandName": "Project",
@ -9,6 +17,14 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -17,7 +17,7 @@
"match": {
"path": "identity/{**catch-all}"
},
"Transforms": [
"Transforms": [
{
"PathRemovePrefix": "identity"
}
@ -28,7 +28,7 @@
"match": {
"path": "flight/{**catch-all}"
},
"Transforms": [
"Transforms": [
{
"PathRemovePrefix": "flight"
}
@ -39,7 +39,7 @@
"match": {
"path": "passenger/{**catch-all}"
},
"Transforms": [
"Transforms": [
{
"PathRemovePrefix": "passenger"
}
@ -50,7 +50,7 @@
"match": {
"path": "booking/{**catch-all}"
},
"Transforms": [
"Transforms": [
{
"PathRemovePrefix": "booking"
}

View File

@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.SDK" Version="13.1.1"/>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>bde28db3-85ba-4201-b889-0f3faba24169</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.1.1" />
<PackageReference Include="Aspire.Hosting.Docker" Version="13.1.1-preview.1.26105.8" />
<PackageReference Include="Aspire.Hosting.MongoDB" Version="13.1.1" />
<PackageReference Include="Aspire.Hosting.PostgreSQL" Version="13.1.1" />
<PackageReference Include="Aspire.Hosting.RabbitMQ" Version="13.1.1" />
<PackageReference Include="Aspire.Hosting.Redis" Version="13.1.1" />
<PackageReference Include="CommunityToolkit.Aspire.Hosting.EventStore" Version="9.9.0" />
<PackageReference Include="Elastic.Aspire.Hosting.Elasticsearch" Version="9.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\ApiGateway\src\ApiGateway.csproj" />
<ProjectReference Include="..\..\..\Services\Booking\src\Booking.Api\Booking.Api.csproj" />
<ProjectReference Include="..\..\..\Services\Flight\src\Flight.Api\Flight.Api.csproj" />
<ProjectReference Include="..\..\..\Services\Identity\src\Identity.Api\Identity.Api.csproj" />
<ProjectReference Include="..\..\..\Services\Passenger\src\Passenger.Api\Passenger.Api.csproj" />
</ItemGroup>
</Project>

View File

@ -1,366 +0,0 @@
using System.Net.Sockets;
var builder = DistributedApplication.CreateBuilder(args);
builder.AddDockerComposeEnvironment("docker-compose");
// 1. Database Services
var pgUsername = builder.AddParameter("pg-username", "postgres", secret: true);
var pgPassword = builder.AddParameter("pg-password", "postgres", secret: true);
var postgres = builder.AddPostgres("postgres", pgUsername, pgPassword)
.WithImage("postgres:latest")
.WithEndpoint(
"tcp",
e =>
{
e.Port = 5432;
e.TargetPort = 5432;
e.IsProxied = true;
e.IsExternal = false;
})
.WithArgs(
"-c",
"wal_level=logical",
"-c",
"max_prepared_transactions=10");
if (builder.ExecutionContext.IsPublishMode)
{
postgres.WithDataVolume("postgres-data")
.WithLifetime(ContainerLifetime.Persistent);
}
var flightDb = postgres.AddDatabase("flight");
var passengerDb = postgres.AddDatabase("passenger");
var identityDb = postgres.AddDatabase("identity");
var persistMessageDb = postgres.AddDatabase("persist-message");
var mongoUsername = builder.AddParameter("mongo-username", "root", secret: true);
var mongoPassword = builder.AddParameter("mongo-password", "secret", secret: true);
var mongo = builder.AddMongoDB("mongo", userName: mongoUsername, password: mongoPassword)
.WithImage("mongo")
.WithImageTag("latest")
.WithEndpoint(
"tcp",
e =>
{
e.Port = 27017;
e.TargetPort = 27017;
e.IsProxied = true;
e.IsExternal = false;
});
if (builder.ExecutionContext.IsPublishMode)
{
mongo.WithDataVolume("mongo-data")
.WithLifetime(ContainerLifetime.Persistent);
}
var redis = builder.AddRedis("redis")
.WithImage("redis:latest")
.WithEndpoint(
"tcp",
e =>
{
e.Port = 6379;
e.TargetPort = 6379;
e.IsProxied = true;
e.IsExternal = false;
});
if (builder.ExecutionContext.IsPublishMode)
{
redis.WithDataVolume("redis-data")
.WithLifetime(ContainerLifetime.Persistent);
}
var eventstore = builder.AddEventStore("eventstore")
.WithImage("eventstore/eventstore")
.WithEnvironment("EVENTSTORE_CLUSTER_SIZE", "1")
.WithEnvironment("EVENTSTORE_RUN_PROJECTIONS", "All")
.WithEnvironment("EVENTSTORE_START_STANDARD_PROJECTIONS", "True")
.WithEnvironment("EVENTSTORE_INSECURE", "True")
.WithEnvironment("EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP", "True")
.WithEndpoint(
"http",
e =>
{
e.TargetPort = 2113;
e.Port = 2113;
e.IsProxied = true;
e.IsExternal = true;
})
.WithEndpoint(
port: 1113,
targetPort: 1113,
name: "tcp",
isProxied: true,
isExternal: false);
if (builder.ExecutionContext.IsPublishMode)
{
eventstore.WithDataVolume("eventstore-data")
.WithLifetime(ContainerLifetime.Persistent);
}
// 2. Messaging Services
var rabbitmqUsername = builder.AddParameter("rabbitmq-username", "guest", secret: true);
var rabbitmqPassword = builder.AddParameter("rabbitmq-password", "guest", secret: true);
var rabbitmq = builder.AddRabbitMQ("rabbitmq", rabbitmqUsername, rabbitmqPassword)
.WithManagementPlugin()
.WithEndpoint(
"tcp",
e =>
{
e.TargetPort = 5672;
e.Port = 5672;
e.IsProxied = true;
e.IsExternal = false;
})
.WithEndpoint(
"management",
e =>
{
e.TargetPort = 15672;
e.Port = 15672;
e.IsProxied = true;
e.IsExternal = true;
});
if (builder.ExecutionContext.IsPublishMode)
{
rabbitmq.WithLifetime(ContainerLifetime.Persistent);
}
// // 3. Observability Services
var jaeger = builder.AddContainer("jaeger-all-in-one", "jaegertracing/all-in-one")
.WithEndpoint(
port: 6831,
targetPort: 6831,
name: "agent",
protocol: ProtocolType.Udp,
isProxied: true,
isExternal: false)
.WithEndpoint(port: 16686, targetPort: 16686, name: "http", isProxied: true, isExternal: true)
.WithEndpoint(port: 14268, targetPort: 14268, name: "collector", isProxied: true, isExternal: false)
.WithEndpoint(port: 14317, targetPort: 4317, name: "otlp-grpc", isProxied: true, isExternal: false)
.WithEndpoint(port: 14318, targetPort: 4318, name: "otlp-http", isProxied: true, isExternal: false);
if (builder.ExecutionContext.IsPublishMode)
{
jaeger.WithLifetime(ContainerLifetime.Persistent);
}
var zipkin = builder.AddContainer("zipkin-all-in-one", "openzipkin/zipkin")
.WithEndpoint(port: 9411, targetPort: 9411, name: "http", isProxied: true, isExternal: true);
if (builder.ExecutionContext.IsPublishMode)
{
zipkin.WithLifetime(ContainerLifetime.Persistent);
}
var otelCollector = builder.AddContainer("otel-collector", "otel/opentelemetry-collector-contrib")
.WithBindMount(
"../../../../deployments/configs/otel-collector-config.yaml",
"/etc/otelcol-contrib/config.yaml",
isReadOnly: true)
.WithArgs("--config=/etc/otelcol-contrib/config.yaml")
.WithEndpoint(port: 11888, targetPort: 1888, name: "otel-pprof", isProxied: true, isExternal: true)
.WithEndpoint(port: 8888, targetPort: 8888, name: "otel-metrics", isProxied: true, isExternal: true)
.WithEndpoint(port: 8889, targetPort: 8889, name: "otel-exporter-metrics", isProxied: true, isExternal: true)
.WithEndpoint(port: 13133, targetPort: 13133, name: "otel-health", isProxied: true, isExternal: true)
.WithEndpoint(port: 4317, targetPort: 4317, name: "otel-grpc", isProxied: true, isExternal: true)
.WithEndpoint(port: 4318, targetPort: 4318, name: "otel-http", isProxied: true, isExternal: true)
.WithEndpoint(port: 55679, targetPort: 55679, name: "otel-zpages", isProxied: true, isExternal: true);
if (builder.ExecutionContext.IsPublishMode)
{
otelCollector.WithLifetime(ContainerLifetime.Persistent);
}
var prometheus = builder.AddContainer("prometheus", "prom/prometheus")
.WithBindMount("../../../../deployments/configs/prometheus.yaml", "/etc/prometheus/prometheus.yml")
.WithArgs(
"--config.file=/etc/prometheus/prometheus.yml",
"--storage.tsdb.path=/prometheus",
"--web.console.libraries=/usr/share/prometheus/console_libraries",
"--web.console.templates=/usr/share/prometheus/consoles",
"--web.enable-remote-write-receiver")
.WithEndpoint(port: 9090, targetPort: 9090, name: "http", isProxied: true, isExternal: true);
if (builder.ExecutionContext.IsPublishMode)
{
prometheus.WithLifetime(ContainerLifetime.Persistent);
}
var grafana = builder.AddContainer("grafana", "grafana/grafana")
.WithEnvironment("GF_INSTALL_PLUGINS", "grafana-clock-panel,grafana-simple-json-datasource")
.WithEnvironment("GF_SECURITY_ADMIN_USER", "admin")
.WithEnvironment("GF_SECURITY_ADMIN_PASSWORD", "admin")
.WithEnvironment("GF_FEATURE_TOGGLES_ENABLE", "traceqlEditor")
.WithBindMount("../../../../deployments/configs/grafana/provisioning", "/etc/grafana/provisioning")
.WithBindMount("../../../../deployments/configs/grafana/dashboards", "/var/lib/grafana/dashboards")
.WithEndpoint(port: 3000, targetPort: 3000, name: "http", isProxied: true, isExternal: true);
if (builder.ExecutionContext.IsPublishMode)
{
grafana.WithLifetime(ContainerLifetime.Persistent);
}
var nodeExporter = builder.AddContainer("node-exporter", "prom/node-exporter")
.WithBindMount("/proc", "/host/proc", isReadOnly: true)
.WithBindMount("/sys", "/host/sys", isReadOnly: true)
.WithBindMount("/", "/rootfs", isReadOnly: true)
.WithArgs(
"--path.procfs=/host/proc",
"--path.rootfs=/rootfs",
"--path.sysfs=/host/sys")
.WithEndpoint(port: 9101, targetPort: 9100, name: "http", isProxied: true, isExternal: true);
if (builder.ExecutionContext.IsPublishMode)
{
nodeExporter.WithLifetime(ContainerLifetime.Persistent);
}
var tempo = builder.AddContainer("tempo", "grafana/tempo")
.WithBindMount("../../../../deployments/configs/tempo.yaml", "/etc/tempo.yaml", isReadOnly: true)
.WithArgs("--config.file=/etc/tempo.yaml")
.WithEndpoint(port: 3200, targetPort: 3200, name: "http", isProxied: true, isExternal: false)
.WithEndpoint(port: 9095, targetPort: 9095, name: "grpc", isProxied: true, isExternal: false)
.WithEndpoint(port: 4317, targetPort: 4317, name: "otlp-grpc", isProxied: true, isExternal: false)
.WithEndpoint(port: 4318, targetPort: 4318, name: "otlp-http", isProxied: true, isExternal: false);
if (builder.ExecutionContext.IsPublishMode)
{
tempo.WithLifetime(ContainerLifetime.Persistent);
}
var loki = builder.AddContainer("loki", "grafana/loki")
.WithBindMount("../../../../deployments/configs/loki-config.yaml", "/etc/loki/local-config.yaml", isReadOnly: true)
.WithArgs("-config.file=/etc/loki/local-config.yaml")
.WithEndpoint(port: 3100, targetPort: 3100, name: "http", isProxied: true, isExternal: false)
.WithEndpoint(port: 9096, targetPort: 9096, name: "grpc", isProxied: true, isExternal: false);
if (builder.ExecutionContext.IsPublishMode)
{
loki.WithLifetime(ContainerLifetime.Persistent);
}
var elasticsearch = builder.AddElasticsearch("elasticsearch")
.WithImage("docker.elastic.co/elasticsearch/elasticsearch:8.17.0")
.WithEnvironment("discovery.type", "single-node")
.WithEnvironment("cluster.name", "docker-cluster")
.WithEnvironment("node.name", "docker-node")
.WithEnvironment("ES_JAVA_OPTS", "-Xms512m -Xmx512m")
.WithEnvironment("xpack.security.enabled", "false")
.WithEnvironment("xpack.security.http.ssl.enabled", "false")
.WithEnvironment("xpack.security.transport.ssl.enabled", "false")
.WithEnvironment("network.host", "0.0.0.0")
.WithEnvironment("http.port", "9200")
.WithEnvironment("transport.host", "localhost")
.WithEnvironment("bootstrap.memory_lock", "true")
.WithEnvironment("cluster.routing.allocation.disk.threshold_enabled", "false")
.WithEndpoint(
"http",
e =>
{
e.TargetPort = 9200;
e.Port = 9200;
e.IsProxied = true;
e.IsExternal = false;
})
.WithEndpoint(
"internal",
e =>
{
e.TargetPort = 9300;
e.Port = 9300;
e.IsProxied = true;
e.IsExternal = false;
})
.WithDataVolume("elastic-data");
if (builder.ExecutionContext.IsPublishMode)
{
elasticsearch.WithLifetime(ContainerLifetime.Persistent);
}
var kibana = builder.AddContainer("kibana", "docker.elastic.co/kibana/kibana:8.17.0")
.WithEnvironment("ELASTICSEARCH_HOSTS", "http://elasticsearch:9200")
.WithEndpoint(port: 5601, targetPort: 5601, name: "http", isProxied: true, isExternal: true)
.WithReference(elasticsearch)
.WaitFor(elasticsearch);
if (builder.ExecutionContext.IsPublishMode)
{
kibana.WithLifetime(ContainerLifetime.Persistent);
}
// 5. Application Services
var identity = builder.AddProject<Projects.Identity_Api>("identity-service")
.WithReference(persistMessageDb)
.WaitFor(persistMessageDb)
.WithReference(identityDb)
.WaitFor(identityDb)
.WithReference(mongo)
.WaitFor(mongo)
.WithReference(rabbitmq)
.WaitFor(rabbitmq)
.WithHttpEndpoint(port: 6005, name: "identity-http")
.WithHttpsEndpoint(port: 5005, name: "identity-https");
var passenger = builder.AddProject<Projects.Passenger_Api>("passenger-service")
.WithReference(persistMessageDb)
.WaitFor(persistMessageDb)
.WithReference(passengerDb)
.WaitFor(passengerDb)
.WithReference(mongo)
.WaitFor(mongo)
.WithReference(rabbitmq)
.WaitFor(rabbitmq)
.WithHttpEndpoint(port: 6012, name: "passenger-http")
.WithHttpsEndpoint(port: 5012, name: "passenger-https");
var flight = builder.AddProject<Projects.Flight_Api>("flight-service")
.WithReference(persistMessageDb)
.WaitFor(persistMessageDb)
.WithReference(flightDb)
.WaitFor(flightDb)
.WithReference(mongo)
.WaitFor(mongo)
.WithReference(rabbitmq)
.WaitFor(rabbitmq)
.WithHttpEndpoint(port: 5004, name: "flight-http")
.WithHttpsEndpoint(port: 5003, name: "flight-https");
var booking = builder.AddProject<Projects.Booking_Api>("booking-service")
.WithReference(persistMessageDb)
.WaitFor(persistMessageDb)
.WithReference(eventstore)
.WaitFor(eventstore)
.WithReference(mongo)
.WaitFor(mongo)
.WithReference(rabbitmq)
.WaitFor(rabbitmq)
.WithHttpEndpoint(port: 6010, name: "booking-http")
.WithHttpsEndpoint(port: 5010, name: "booking-https");
var gateway = builder.AddProject<Projects.ApiGateway>("api-gateway")
.WithReference(flight)
.WaitFor(flight)
.WithReference(passenger)
.WaitFor(passenger)
.WithReference(identity)
.WaitFor(identity)
.WithReference(booking)
.WaitFor(booking)
.WithHttpEndpoint(port: 5001, name: "gateway-http")
.WithHttpsEndpoint(port: 5000, name: "gateway-https");
builder.Build().Run();

View File

@ -1,18 +0,0 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"AppHost": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:18888",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://otel-collector:4317",
"ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "http://otel-collector:4318"
}
}
}
}

View File

@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -1,41 +0,0 @@
using BuildingBlocks.HealthCheck;
using BuildingBlocks.OpenTelemetryCollector;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ServiceDefaults;
public static class Extensions
{
public static IHostApplicationBuilder AddServiceDefaults(this WebApplicationBuilder builder)
{
builder.Services.AddCustomHealthCheck();
builder.AddCustomObservability();
builder.Services.AddServiceDiscovery();
builder.Services.ConfigureHttpClientDefaults(http =>
{
http.AddStandardResilienceHandler(options =>
{
var timeSpan = TimeSpan.FromMinutes(1);
options.CircuitBreaker.SamplingDuration = timeSpan * 2;
options.TotalRequestTimeout.Timeout = timeSpan * 3;
options.Retry.MaxRetryAttempts = 3;
});
// Turn on service discovery by default
http.AddServiceDiscovery();
});
return builder;
}
public static WebApplication UseServiceDefaults(this WebApplication app)
{
app.UseCustomHealthCheck();
app.UseCustomObservability();
return app;
}
}

View File

@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\BuildingBlocks.csproj" IsAspireProjectResource="false" />
</ItemGroup>
</Project>

View File

@ -1,110 +1,110 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" Version="5.0.0" />
<PackageReference Include="Ardalis.GuardClauses" Version="4.6.0" />
<PackageReference Include="Asp.Versioning.Abstractions" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Http" Version="8.1.1" />
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.1" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.1" />
<PackageReference Include="Figgle.Fonts" Version="0.6.5" />
<PackageReference Include="Asp.Versioning.Http" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.8" />
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.Elasticsearch" Version="8.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.EventStore" Version="8.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="8.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="8.0.2" />
<PackageReference Include="AspNetCore.HealthChecks.UI.SQLite.Storage" Version="8.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="8.1.0" />
<PackageReference Include="Grpc.Core.Testing" Version="2.46.6" />
<PackageReference Include="EasyCaching.Core" Version="1.9.2" />
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
<PackageReference Include="EasyNetQ.Management.Client" Version="3.0.1" />
<PackageReference Include="EFCore.NamingConventions" Version="10.0.1" />
<PackageReference Include="Figgle" Version="0.6.5" />
<PackageReference Include="FluentValidation" Version="12.1.1" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.3.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="10.3.0" />
<PackageReference Include="Npgsql" Version="10.0.1" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="Polly" Version="8.6.5" />
<PackageReference Include="Humanizer.Core" Version="3.0.1" />
<PackageReference Include="EasyNetQ.Management.Client" Version="3.0.0" />
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="Figgle" Version="0.5.1" />
<PackageReference Include="FluentValidation" Version="11.10.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
<PackageReference Include="Npgsql" Version="8.0.4" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.9.0-beta.1" />
<PackageReference Include="Polly" Version="8.4.1" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="IdGen" Version="3.0.7" />
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
<PackageReference Include="MediatR" Version="14.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
<PackageReference Include="MongoDB.Driver" Version="3.6.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Scalar.AspNetCore" Version="2.12.38" />
<PackageReference Include="Scrutor" Version="7.0.0" />
<PackageReference Include="MediatR" Version="12.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
<PackageReference Include="Scrutor" Version="4.2.2" />
<PackageReference Include="Sentry.Serilog" Version="4.10.2" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" />
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.5" />
<PackageReference Include="Sieve" Version="2.5.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.2" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.1.2" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.1.2" />
<PackageReference Include="MassTransit" Version="8.5.8" />
<PackageReference Include="MassTransit.RabbitMQ" Version="8.5.8" />
<PackageReference Include="Duende.IdentityServer" Version="7.4.5" />
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.4.5" />
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.4.5" />
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.4.5" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.3" />
<PackageReference Include="System.Linq.Async" Version="7.0.0" />
<PackageReference Include="System.Linq.Async.Queryable" Version="7.0.0" />
<PackageReference Include="Testcontainers" Version="4.9.0" />
<PackageReference Include="Testcontainers.EventStoreDb" Version="4.9.0" />
<PackageReference Include="Testcontainers.MongoDb" Version="4.9.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="4.9.0" />
<PackageReference Include="Testcontainers.RabbitMq" Version="4.9.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.3" />
<PackageReference Include="Xunit.Extensions.Logging" Version="1.1.0" />
<PackageReference Include="Yarp.ReverseProxy" Version="2.3.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.3" />
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.EventStore" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.3" />
<PackageReference Include="MassTransit" Version="8.2.5" />
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.5" />
<PackageReference Include="Duende.IdentityServer" Version="7.0.6" />
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.0.6" />
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.0.6" />
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Linq.Async.Queryable" Version="6.0.1" />
<PackageReference Include="Testcontainers" Version="3.10.0" />
<PackageReference Include="Testcontainers.EventStoreDb" Version="3.10.0" />
<PackageReference Include="Testcontainers.MongoDb" Version="3.10.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.10.0" />
<PackageReference Include="Testcontainers.RabbitMq" Version="3.10.0" />
<PackageReference Include="Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.7.1" />
<PackageReference Include="Yarp.ReverseProxy" Version="2.2.0" />
<PackageReference Include="Npgsql.OpenTelemetry" Version="10.0.1" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.15.0-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="1.15.0-beta.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.0" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.15.0-beta.1" />
<PackageReference Include="Grafana.OpenTelemetry" Version="1.5.2" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.15.0" />
<PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.15.0" />
<PackageReference Include="prometheus-net" Version="8.2.1" />
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1" />
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.9" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
<PackageReference Include="OpenTelemetry" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.Jaeger" Version="1.6.0-rc.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.5" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="AutoBogus" Version="2.13.1" />
<PackageReference Include="Bogus" Version="35.6.5" />
<PackageReference Include="FluentAssertions" Version="8.8.0" />
<PackageReference Include="Respawn" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="10.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.3" />
<PackageReference Include="WebMotions.Fake.Authentication.JwtBearer" Version="10.0.0" />
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="FluentAssertions" Version="6.12.1" />
<PackageReference Include="Respawn" Version="6.2.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" />
<PackageReference Include="WebMotions.Fake.Authentication.JwtBearer" Version="8.0.1" />
<PackageReference Include="Google.Protobuf" Version="3.33.5" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.76.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Google.Protobuf" Version="3.28.1" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.65.0" />
</ItemGroup>
</Project>

View File

@ -47,4 +47,4 @@ public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest,
return response;
}
}
}

View File

@ -4,4 +4,4 @@ public interface ICacheRequest
{
string CacheKey { get; }
DateTime? AbsoluteExpirationRelativeToNow { get; }
}
}

View File

@ -4,4 +4,4 @@ namespace BuildingBlocks.Caching
{
string CacheKey { get; }
}
}
}

View File

@ -36,4 +36,4 @@ namespace BuildingBlocks.Caching
return response;
}
}
}
}

View File

@ -1,10 +0,0 @@
namespace BuildingBlocks.Constants;
public static class IdentityConstant
{
public static class Role
{
public const string Admin = "admin";
public const string User = "user";
}
}

View File

@ -8,4 +8,4 @@ public record FlightDeleted(Guid Id) : IIntegrationEvent;
public record AircraftCreated(Guid Id) : IIntegrationEvent;
public record AirportCreated(Guid Id) : IIntegrationEvent;
public record SeatCreated(Guid Id) : IIntegrationEvent;
public record SeatReserved(Guid Id) : IIntegrationEvent;
public record SeatReserved(Guid Id) : IIntegrationEvent;

View File

@ -2,4 +2,4 @@ using BuildingBlocks.Core.Event;
namespace BuildingBlocks.Contracts.EventBus.Messages;
public record UserCreated(Guid Id, string Name, string PassportNumber) : IIntegrationEvent;
public record UserCreated(Guid Id, string Name, string PassportNumber) : IIntegrationEvent;

View File

@ -3,4 +3,4 @@ using BuildingBlocks.Core.Event;
namespace BuildingBlocks.Contracts.EventBus.Messages;
public record PassengerRegistrationCompleted(Guid Id) : IIntegrationEvent;
public record PassengerCreated(Guid Id) : IIntegrationEvent;
public record PassengerCreated(Guid Id) : IIntegrationEvent;

View File

@ -2,4 +2,4 @@ using BuildingBlocks.Core.Event;
namespace BuildingBlocks.Contracts.EventBus.Messages;
public record BookingCreated(Guid Id) : IIntegrationEvent;
public record BookingCreated(Guid Id) : IIntegrationEvent;

View File

@ -9,4 +9,4 @@ public interface ICommand : ICommand<Unit>
public interface ICommand<out T> : IRequest<T>
where T : notnull
{
}
}

View File

@ -11,4 +11,4 @@ public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TComm
where TCommand : ICommand<TResponse>
where TResponse : notnull
{
}
}

View File

@ -5,4 +5,4 @@ namespace BuildingBlocks.Core.CQRS;
public interface IQuery<out T> : IRequest<T>
where T : notnull
{
}
}

View File

@ -6,4 +6,4 @@ public interface IQueryHandler<in TQuery, TResponse> : IRequestHandler<TQuery, T
where TQuery : IQuery<TResponse>
where TResponse : notnull
{
}
}

View File

@ -1,37 +0,0 @@
using BuildingBlocks.Core.Event;
namespace BuildingBlocks.Core;
public class CompositeEventMapper : IEventMapper
{
private readonly IEnumerable<IEventMapper> _mappers;
public CompositeEventMapper(IEnumerable<IEventMapper> mappers)
{
_mappers = mappers;
}
public IIntegrationEvent? MapToIntegrationEvent(IDomainEvent @event)
{
foreach (var mapper in _mappers)
{
var integrationEvent = mapper.MapToIntegrationEvent(@event);
if (integrationEvent is not null)
return integrationEvent;
}
return null;
}
public IInternalCommand? MapToInternalCommand(IDomainEvent @event)
{
foreach (var mapper in _mappers)
{
var internalCommand = mapper.MapToInternalCommand(@event);
if (internalCommand is not null)
return internalCommand;
}
return null;
}
}

View File

@ -6,4 +6,4 @@ public enum EventType
DomainEvent = 1,
IntegrationEvent = 2,
InternalCommand = 4
}
}

View File

@ -2,4 +2,4 @@ namespace BuildingBlocks.Core.Event;
public interface IDomainEvent : IEvent
{
}
}

View File

@ -9,4 +9,4 @@ public interface IEvent : INotification
Guid EventId => NewId.NextGuid();
public DateTime OccurredOn => DateTime.Now;
public string EventType => GetType().AssemblyQualifiedName;
}
}

View File

@ -2,4 +2,4 @@ namespace BuildingBlocks.Core.Event;
public interface IHaveIntegrationEvent
{
}
}

View File

@ -5,4 +5,4 @@ namespace BuildingBlocks.Core.Event;
[ExcludeFromTopology]
public interface IIntegrationEvent : IEvent
{
}
}

View File

@ -2,4 +2,4 @@ namespace BuildingBlocks.Core.Event;
public interface IInternalCommand : IEvent
{
}
}

View File

@ -2,4 +2,4 @@ using BuildingBlocks.Core.CQRS;
namespace BuildingBlocks.Core.Event;
public record InternalCommand : IInternalCommand, ICommand;
public record InternalCommand : IInternalCommand, ICommand;

View File

@ -23,4 +23,4 @@ public class MessageEnvelope<TMessage> : MessageEnvelope
}
public new TMessage? Message { get; }
}
}

View File

@ -9,17 +9,30 @@ using MessageEnvelope = BuildingBlocks.Core.Event.MessageEnvelope;
namespace BuildingBlocks.Core;
public sealed class EventDispatcher(
IServiceScopeFactory serviceScopeFactory,
IEventMapper eventMapper,
ILogger<EventDispatcher> logger,
IPersistMessageProcessor persistMessageProcessor,
IHttpContextAccessor httpContextAccessor
)
: IEventDispatcher
public sealed class EventDispatcher : IEventDispatcher
{
private readonly IEventMapper _eventMapper;
private readonly ILogger<EventDispatcher> _logger;
private readonly IPersistMessageProcessor _persistMessageProcessor;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IServiceScopeFactory _serviceScopeFactory;
public EventDispatcher(IServiceScopeFactory serviceScopeFactory,
IEventMapper eventMapper,
ILogger<EventDispatcher> logger,
IPersistMessageProcessor persistMessageProcessor,
IHttpContextAccessor httpContextAccessor)
{
_serviceScopeFactory = serviceScopeFactory;
_eventMapper = eventMapper;
_logger = logger;
_persistMessageProcessor = persistMessageProcessor;
_httpContextAccessor = httpContextAccessor;
}
public async Task SendAsync<T>(IReadOnlyList<T> events, Type type = null,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
where T : IEvent
{
if (events.Count > 0)
@ -32,7 +45,7 @@ public sealed class EventDispatcher(
{
foreach (var integrationEvent in integrationEvents)
{
await persistMessageProcessor.PublishMessageAsync(
await _persistMessageProcessor.PublishMessageAsync(
new MessageEnvelope(integrationEvent, SetHeaders()),
cancellationToken);
}
@ -61,7 +74,7 @@ public sealed class EventDispatcher(
foreach (var internalMessage in internalMessages)
{
await persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
await _persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
}
}
}
@ -76,20 +89,20 @@ public sealed class EventDispatcher(
private Task<IReadOnlyList<IIntegrationEvent>> MapDomainEventToIntegrationEventAsync(
IReadOnlyList<IDomainEvent> events)
{
logger.LogTrace("Processing integration events start...");
_logger.LogTrace("Processing integration events start...");
var wrappedIntegrationEvents = GetWrappedIntegrationEvents(events.ToList())?.ToList();
if (wrappedIntegrationEvents?.Count > 0)
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(wrappedIntegrationEvents);
var integrationEvents = new List<IIntegrationEvent>();
using var scope = serviceScopeFactory.CreateScope();
using var scope = _serviceScopeFactory.CreateScope();
foreach (var @event in events)
{
var eventType = @event.GetType();
logger.LogTrace($"Handling domain event: {eventType.Name}");
_logger.LogTrace($"Handling domain event: {eventType.Name}");
var integrationEvent = eventMapper.MapToIntegrationEvent(@event);
var integrationEvent = _eventMapper.MapToIntegrationEvent(@event);
if (integrationEvent is null)
continue;
@ -97,7 +110,7 @@ public sealed class EventDispatcher(
integrationEvents.Add(integrationEvent);
}
logger.LogTrace("Processing integration events done...");
_logger.LogTrace("Processing integration events done...");
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(integrationEvents);
}
@ -106,16 +119,16 @@ public sealed class EventDispatcher(
private Task<IReadOnlyList<IInternalCommand>> MapDomainEventToInternalCommandAsync(
IReadOnlyList<IDomainEvent> events)
{
logger.LogTrace("Processing internal message start...");
_logger.LogTrace("Processing internal message start...");
var internalCommands = new List<IInternalCommand>();
using var scope = serviceScopeFactory.CreateScope();
using var scope = _serviceScopeFactory.CreateScope();
foreach (var @event in events)
{
var eventType = @event.GetType();
logger.LogTrace($"Handling domain event: {eventType.Name}");
_logger.LogTrace($"Handling domain event: {eventType.Name}");
var integrationEvent = eventMapper.MapToInternalCommand(@event);
var integrationEvent = _eventMapper.MapToInternalCommand(@event);
if (integrationEvent is null)
continue;
@ -123,7 +136,7 @@ public sealed class EventDispatcher(
internalCommands.Add(integrationEvent);
}
logger.LogTrace("Processing internal message done...");
_logger.LogTrace("Processing internal message done...");
return Task.FromResult<IReadOnlyList<IInternalCommand>>(internalCommands);
}
@ -146,10 +159,10 @@ public sealed class EventDispatcher(
private IDictionary<string, object> SetHeaders()
{
var headers = new Dictionary<string, object>();
headers.Add("CorrelationId", httpContextAccessor?.HttpContext?.GetCorrelationId());
headers.Add("UserId", httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier));
headers.Add("UserName", httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.Name));
headers.Add("CorrelationId", _httpContextAccessor?.HttpContext?.GetCorrelationId());
headers.Add("UserId", _httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier));
headers.Add("UserName", _httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.Name));
return headers;
}
}
}

View File

@ -8,4 +8,4 @@ public interface IEventDispatcher
where T : IEvent;
public Task SendAsync<T>(T @event, Type type = null, CancellationToken cancellationToken = default)
where T : IEvent;
}
}

View File

@ -6,4 +6,4 @@ public interface IEventMapper
{
IIntegrationEvent? MapToIntegrationEvent(IDomainEvent @event);
IInternalCommand? MapToInternalCommand(IDomainEvent @event);
}
}

View File

@ -3,4 +3,4 @@ using BuildingBlocks.Core.Event;
namespace BuildingBlocks.Core;
public record IntegrationEventWrapper<TDomainEventType>(TDomainEventType DomainEvent) : IIntegrationEvent
where TDomainEventType : IDomainEvent;
where TDomainEventType : IDomainEvent;

View File

@ -20,4 +20,4 @@ public abstract record Aggregate<TId> : Entity<TId>, IAggregate<TId>
return dequeuedEvents;
}
}
}

View File

@ -9,4 +9,4 @@ public abstract record Entity<T> : IEntity<T>
public long? LastModifiedBy { get; set; }
public bool IsDeleted { get; set; }
public long Version { get; set; }
}
}

View File

@ -10,4 +10,4 @@ public interface IAggregate : IEntity
{
IReadOnlyList<IDomainEvent> DomainEvents { get; }
IEvent[] ClearDomainEvents();
}
}

View File

@ -12,4 +12,4 @@ public interface IEntity : IVersion
public DateTime? LastModified { get; set; }
public long? LastModifiedBy { get; set; }
public bool IsDeleted { get; set; }
}
}

View File

@ -4,4 +4,4 @@ namespace BuildingBlocks.Core.Model;
public interface IVersion
{
long Version { get; set; }
}
}

View File

@ -33,4 +33,4 @@ public static class Extensions
return PageList<TEntity>.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
}
}
}

View File

@ -13,4 +13,4 @@ public interface IPageList<T>
int TotalCount { get; init; }
int PageNumber { get; init; }
int PageSize { get; init; }
}
}

View File

@ -4,4 +4,4 @@ using MediatR;
public interface IPageQuery<out TResponse> : IPageRequest, IRequest<TResponse>
where TResponse : class
{ }
{ }

View File

@ -6,4 +6,4 @@ public interface IPageRequest
int PageSize { get; init; }
string? Filters { get; init; }
string? SortOrder { get; init; }
}
}

View File

@ -16,4 +16,4 @@ public record PageList<T>(IReadOnlyList<T> Items, int PageNumber, int PageSize,
{
return new PageList<T>(items, pageNumber, pageSize, totalItems);
}
}
}

View File

@ -1,14 +1,15 @@
namespace BuildingBlocks.EFCore;
using System.Collections.Immutable;
using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model;
using BuildingBlocks.Web;
using Core.Event;
using Core.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Logging;
using Web;
using Exception = System.Exception;
using IsolationLevel = System.Data.IsolationLevel;
namespace BuildingBlocks.EFCore;
public abstract class AppDbContextBase : DbContext, IDbContext
{
private readonly ICurrentUserProvider? _currentUserProvider;
@ -173,9 +174,9 @@ public abstract class AppDbContextBase : DbContext, IDbContext
}
}
}
catch (System.Exception ex)
catch (Exception ex)
{
throw new System.Exception("try for find IAggregate", ex);
throw new Exception("try for find IAggregate", ex);
}
}
}
}

View File

@ -1,3 +1,5 @@
using System;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
@ -57,4 +59,4 @@ namespace BuildingBlocks.EFCore
return CreateNewInstance(options);
}
}
}
}

View File

@ -1,39 +1,50 @@
using System.Text.Json;
using System.Transactions;
using BuildingBlocks.Core;
using BuildingBlocks.PersistMessageProcessor;
using BuildingBlocks.Polly;
using MediatR;
using Microsoft.Extensions.Logging;
namespace BuildingBlocks.EFCore;
using System.Transactions;
using PersistMessageProcessor;
using Polly;
public class EfTxBehavior<TRequest, TResponse>(
ILogger<EfTxBehavior<TRequest, TResponse>> logger,
IDbContext dbContextBase,
IPersistMessageDbContext persistMessageDbContext,
IEventDispatcher eventDispatcher
)
: IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull, IRequest<TResponse>
where TResponse : notnull
public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull, IRequest<TResponse>
where TResponse : notnull
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
private readonly ILogger<EfTxBehavior<TRequest, TResponse>> _logger;
private readonly IDbContext _dbContextBase;
private readonly IPersistMessageDbContext _persistMessageDbContext;
private readonly IEventDispatcher _eventDispatcher;
public EfTxBehavior(
ILogger<EfTxBehavior<TRequest, TResponse>> logger,
IDbContext dbContextBase,
IPersistMessageDbContext persistMessageDbContext,
IEventDispatcher eventDispatcher)
{
logger.LogInformation(
_logger = logger;
_dbContextBase = dbContextBase;
_persistMessageDbContext = persistMessageDbContext;
_eventDispatcher = eventDispatcher;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
_logger.LogInformation(
"{Prefix} Handled command {MediatrRequest}",
nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName);
logger.LogDebug(
_logger.LogDebug(
"{Prefix} Handled command {MediatrRequest} with content {RequestContent}",
nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName,
JsonSerializer.Serialize(request));
logger.LogInformation(
_logger.LogInformation(
"{Prefix} Open the transaction for {MediatrRequest}",
nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName);
@ -45,32 +56,32 @@ where TResponse : notnull
var response = await next();
logger.LogInformation(
_logger.LogInformation(
"{Prefix} Executed the {MediatrRequest} request",
nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName);
while (true)
{
var domainEvents = dbContextBase.GetDomainEvents();
var domainEvents = _dbContextBase.GetDomainEvents();
if (domainEvents is null || !domainEvents.Any())
{
return response;
}
await eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
await _eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
// Save data to database with some retry policy in distributed transaction
await dbContextBase.RetryOnFailure(async () =>
await _dbContextBase.RetryOnFailure(async () =>
{
await dbContextBase.SaveChangesAsync(cancellationToken);
await _dbContextBase.SaveChangesAsync(cancellationToken);
});
// Save data to database with some retry policy in distributed transaction
await persistMessageDbContext.RetryOnFailure(async () =>
await _persistMessageDbContext.RetryOnFailure(async () =>
{
await persistMessageDbContext.SaveChangesAsync(cancellationToken);
await _persistMessageDbContext.SaveChangesAsync(cancellationToken);
});
scope.Complete();
@ -78,4 +89,4 @@ where TResponse : notnull
return response;
}
}
}
}

View File

@ -1,65 +1,67 @@
using System.Linq.Expressions;
using BuildingBlocks.Core.Model;
using BuildingBlocks.Web;
using Humanizer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;
namespace BuildingBlocks.EFCore;
using Ardalis.GuardClauses;
using Humanizer;
using Microsoft.EntityFrameworkCore.Metadata;
public static class Extensions
{
public static IServiceCollection AddCustomDbContext<TContext>(this WebApplicationBuilder builder, string? connectionName = "")
public static IServiceCollection AddCustomDbContext<TContext>(this IServiceCollection services)
where TContext : DbContext, IDbContext
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
builder.Services.AddValidateOptions<PostgresOptions>();
services.AddValidateOptions<PostgresOptions>();
builder.Services.AddDbContext<TContext>(
services.AddDbContext<TContext>(
(sp, options) =>
{
var aspireConnectionString = builder.Configuration.GetConnectionString(connectionName.Kebaberize());
var connectionString = aspireConnectionString ?? sp.GetRequiredService<PostgresOptions>().ConnectionString;
var postgresOptions = sp.GetRequiredService<PostgresOptions>();
ArgumentException.ThrowIfNullOrEmpty(connectionString);
Guard.Against.Null(options, nameof(postgresOptions));
options.UseNpgsql(
connectionString,
postgresOptions?.ConnectionString,
dbOptions =>
{
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
})
// https://github.com/efcore/EFCore.NamingConventions
.UseSnakeCaseNamingConvention();
// Suppress warnings for pending model changes
options.ConfigureWarnings(
w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
});
builder.Services.AddScoped<ISeedManager, SeedManager>();
builder.Services.AddScoped<IDbContext>(sp => sp.GetRequiredService<TContext>());
services.AddScoped<IDbContext>(provider => provider.GetService<TContext>());
return builder.Services;
return services;
}
public static IApplicationBuilder UseMigration<TContext>(this IApplicationBuilder app)
public static IApplicationBuilder UseMigration<TContext>(
this IApplicationBuilder app,
IWebHostEnvironment env
)
where TContext : DbContext, IDbContext
{
MigrateAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
MigrateDatabaseAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
SeedAsync(app.ApplicationServices).GetAwaiter().GetResult();
if (!env.IsEnvironment("test"))
{
SeedDataAsync(app.ApplicationServices).GetAwaiter().GetResult();
}
return app;
}
// ref: https://github.com/pdevito3/MessageBusTestingInMemHarness/blob/main/RecipeManagement/src/RecipeManagement/Databases/RecipesDbContext.cs
public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder)
{
@ -112,30 +114,23 @@ public static class Extensions
}
}
private static async Task MigrateAsync<TContext>(IServiceProvider serviceProvider)
private static async Task MigrateDatabaseAsync<TContext>(IServiceProvider serviceProvider)
where TContext : DbContext, IDbContext
{
await using var scope = serviceProvider.CreateAsyncScope();
using var scope = serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<TContext>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TContext>>();
await context.Database.MigrateAsync();
}
var pendingMigrations = await context.Database.GetPendingMigrationsAsync();
private static async Task SeedDataAsync(IServiceProvider serviceProvider)
{
using var scope = serviceProvider.CreateScope();
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
if (pendingMigrations.Any())
foreach (var seeder in seeders)
{
logger.LogInformation("Applying {Count} pending migrations...", pendingMigrations.Count());
await context.Database.MigrateAsync();
logger.LogInformation("Migrations applied successfully.");
await seeder.SeedAllAsync();
}
}
private static async Task SeedAsync(IServiceProvider serviceProvider)
{
await using var scope = serviceProvider.CreateAsyncScope();
var seedersManager = scope.ServiceProvider.GetRequiredService<ISeedManager>();
await seedersManager.ExecuteSeedAsync();
}
}
}

View File

@ -4,9 +4,4 @@ namespace BuildingBlocks.EFCore
{
Task SeedAllAsync();
}
public interface ITestDataSeeder
{
Task SeedAllAsync();
}
}
}

View File

@ -15,4 +15,4 @@ public interface IDbContext
Task RollbackTransactionAsync(CancellationToken cancellationToken = default);
IExecutionStrategy CreateExecutionStrategy();
Task ExecuteTransactionalAsync(CancellationToken cancellationToken = default);
}
}

View File

@ -1,7 +0,0 @@
namespace BuildingBlocks.EFCore;
public interface ISeedManager
{
Task ExecuteSeedAsync();
Task ExecuteTestSeedAsync();
}

View File

@ -3,4 +3,4 @@ namespace BuildingBlocks.EFCore;
public class PostgresOptions
{
public string ConnectionString { get; set; }
}
}

View File

@ -1,42 +0,0 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace BuildingBlocks.EFCore;
public class SeedManager(
ILogger<SeedManager> logger,
IWebHostEnvironment env,
IServiceProvider serviceProvider
) : ISeedManager
{
public async Task ExecuteSeedAsync()
{
if (!env.IsEnvironment("test"))
{
await using var scope = serviceProvider.CreateAsyncScope();
var dataSeeders = scope.ServiceProvider.GetServices<IDataSeeder>();
foreach (var seeder in dataSeeders)
{
logger.LogInformation("Seed {SeederName} is started.", seeder.GetType().Name);
await seeder.SeedAllAsync();
logger.LogInformation("Seed {SeederName} is completed.", seeder.GetType().Name);
}
}
}
public async Task ExecuteTestSeedAsync()
{
await using var scope = serviceProvider.CreateAsyncScope();
var testDataSeeders = scope.ServiceProvider.GetServices<ITestDataSeeder>();
foreach (var testSeeder in testDataSeeders)
{
logger.LogInformation("Seed {SeederName} is started.", testSeeder.GetType().Name);
await testSeeder.SeedAllAsync();
logger.LogInformation("Seed {SeederName} is completed.", testSeeder.GetType().Name);
}
}
}

View File

@ -25,4 +25,4 @@ public class BackgroundWorker : BackgroundService
await perform(stoppingToken);
logger.LogInformation("Background worker stopped");
}, stoppingToken);
}
}

View File

@ -24,16 +24,15 @@ public record EventStoreDBOptions(
public static class EventStoreDBConfigExtensions
{
public static IServiceCollection AddEventStoreDB(this IServiceCollection services, IConfiguration configuration,
public static IServiceCollection AddEventStoreDB(this IServiceCollection services, IConfiguration config,
EventStoreDBOptions? options = null)
{
services
.AddSingleton(x =>
{
var aspireConnectionString = configuration.GetConnectionString("eventstore");
var eventStoreOptions = services.GetOptions<EventStoreOptions>(nameof(EventStoreOptions));
return new EventStoreClient(EventStoreClientSettings.Create(aspireConnectionString ?? eventStoreOptions.ConnectionString));
return new EventStoreClient(EventStoreClientSettings.Create(eventStoreOptions.ConnectionString));
})
.AddScoped(typeof(IEventStoreDBRepository<>), typeof(EventStoreDBRepository<>))
.AddTransient<EventStoreDBSubscriptionToAll, EventStoreDBSubscriptionToAll>();
@ -90,4 +89,4 @@ public static class EventStoreDBConfigExtensions
.AsImplementedInterfaces()
.WithTransientLifetime());
}
}
}

Some files were not shown because too many files have changed in this diff Show More