├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md └── spring-boot ├── domain-model ├── identity │ ├── adapters │ │ └── build.gradle.kts │ ├── api │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── commerce │ │ │ │ └── identity │ │ │ │ └── IdentityApiApp.java │ │ │ └── resources │ │ │ └── application.yaml │ ├── contracts │ │ └── build.gradle.kts │ ├── model │ │ └── build.gradle.kts │ └── unit-tests │ │ ├── build.gradle.kts │ │ └── src │ │ └── test │ │ └── java │ │ └── test │ │ └── commerce │ │ └── identity │ │ └── IdentityApiApp_specs.java ├── inventory │ ├── adapters │ │ └── build.gradle.kts │ ├── contracts │ │ └── build.gradle.kts │ ├── model │ │ └── build.gradle.kts │ └── unit-tests │ │ └── build.gradle.kts └── shared-hosts │ ├── api │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── java │ │ └── commerce │ │ │ └── SharedApiApp.java │ │ └── resources │ │ └── application.yaml │ └── unit-tests │ ├── build.gradle.kts │ └── src │ └── test │ └── java │ └── test │ └── commerce │ └── SharedApiApp_specs.java ├── gateways ├── administration-bff │ ├── api │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── commerce │ │ │ │ └── administrationbff │ │ │ │ └── AdministrationBffApp.java │ │ │ └── resources │ │ │ └── application.yaml │ └── unit-tests │ │ ├── build.gradle.kts │ │ └── src │ │ └── test │ │ └── java │ │ └── test │ │ └── commerce │ │ └── administrationbff │ │ └── AdministrationBffApp_specs.java ├── seller-bff │ ├── api │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── commerce │ │ │ │ └── sellerbff │ │ │ │ └── SellerBffApp.java │ │ │ └── resources │ │ │ └── application.yaml │ └── unit-tests │ │ ├── build.gradle.kts │ │ └── src │ │ └── test │ │ └── java │ │ └── test │ │ └── commerce │ │ └── sellerbff │ │ └── SellerBffApp_specs.java └── shopper-bff │ ├── api │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── java │ │ └── commerce │ │ │ └── shopperbff │ │ │ └── ShopperBffApp.java │ │ └── resources │ │ └── application.yaml │ └── unit-tests │ ├── build.gradle.kts │ └── src │ └── test │ └── java │ └── test │ └── commerce │ └── shopperbff │ └── ShopperBffApp_specs.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.java] 4 | indent_size = 4 5 | tab_width = 4 6 | ij_continuation_indent_size = 4 7 | ij_java_align_consecutive_assignments = false 8 | ij_java_align_consecutive_variable_declarations = false 9 | ij_java_align_group_field_declarations = false 10 | ij_java_align_multiline_annotation_parameters = false 11 | ij_java_align_multiline_array_initializer_expression = false 12 | ij_java_align_multiline_assignment = false 13 | ij_java_align_multiline_binary_operation = false 14 | ij_java_align_multiline_chained_methods = false 15 | ij_java_align_multiline_deconstruction_list_components = true 16 | ij_java_align_multiline_extends_list = false 17 | ij_java_align_multiline_for = true 18 | ij_java_align_multiline_method_parentheses = false 19 | ij_java_align_multiline_parameters = true 20 | ij_java_align_multiline_parameters_in_calls = false 21 | ij_java_align_multiline_parenthesized_expression = false 22 | ij_java_align_multiline_records = true 23 | ij_java_align_multiline_resources = true 24 | ij_java_align_multiline_ternary_operation = false 25 | ij_java_align_multiline_text_blocks = false 26 | ij_java_align_multiline_throws_list = false 27 | ij_java_align_subsequent_simple_methods = false 28 | ij_java_align_throws_keyword = false 29 | ij_java_align_types_in_multi_catch = true 30 | ij_java_annotation_parameter_wrap = off 31 | ij_java_array_initializer_new_line_after_left_brace = false 32 | ij_java_array_initializer_right_brace_on_new_line = false 33 | ij_java_array_initializer_wrap = off 34 | ij_java_assert_statement_colon_on_next_line = false 35 | ij_java_assert_statement_wrap = off 36 | ij_java_assignment_wrap = off 37 | ij_java_binary_operation_sign_on_next_line = false 38 | ij_java_binary_operation_wrap = off 39 | ij_java_blank_lines_after_anonymous_class_header = 0 40 | ij_java_blank_lines_after_class_header = 1 41 | ij_java_blank_lines_after_imports = 1 42 | ij_java_blank_lines_after_package = 1 43 | ij_java_blank_lines_around_class = 1 44 | ij_java_blank_lines_around_field = 0 45 | ij_java_blank_lines_around_field_in_interface = 0 46 | ij_java_blank_lines_around_initializer = 1 47 | ij_java_blank_lines_around_method = 1 48 | ij_java_blank_lines_around_method_in_interface = 1 49 | ij_java_blank_lines_before_class_end = 0 50 | ij_java_blank_lines_before_imports = 1 51 | ij_java_blank_lines_before_method_body = 0 52 | ij_java_blank_lines_before_package = 0 53 | ij_java_block_brace_style = end_of_line 54 | ij_java_block_comment_add_space = false 55 | ij_java_block_comment_at_first_column = true 56 | ij_java_builder_methods = 57 | ij_java_call_parameters_new_line_after_left_paren = true 58 | ij_java_call_parameters_right_paren_on_new_line = true 59 | ij_java_call_parameters_wrap = off 60 | ij_java_case_statement_on_separate_line = true 61 | ij_java_catch_on_new_line = false 62 | ij_java_class_annotation_wrap = split_into_lines 63 | ij_java_class_brace_style = end_of_line 64 | ij_java_class_count_to_use_import_on_demand = 2147483647 65 | ij_java_class_names_in_javadoc = 1 66 | ij_java_deconstruction_list_wrap = normal 67 | ij_java_do_not_indent_top_level_class_members = false 68 | ij_java_do_not_wrap_after_single_annotation = false 69 | ij_java_do_not_wrap_after_single_annotation_in_parameter = false 70 | ij_java_do_while_brace_force = always 71 | ij_java_doc_add_blank_line_after_description = true 72 | ij_java_doc_add_blank_line_after_param_comments = false 73 | ij_java_doc_add_blank_line_after_return = false 74 | ij_java_doc_add_p_tag_on_empty_lines = true 75 | ij_java_doc_align_exception_comments = true 76 | ij_java_doc_align_param_comments = true 77 | ij_java_doc_do_not_wrap_if_one_line = false 78 | ij_java_doc_enable_formatting = true 79 | ij_java_doc_enable_leading_asterisks = true 80 | ij_java_doc_indent_on_continuation = false 81 | ij_java_doc_keep_empty_lines = true 82 | ij_java_doc_keep_empty_parameter_tag = true 83 | ij_java_doc_keep_empty_return_tag = true 84 | ij_java_doc_keep_empty_throws_tag = true 85 | ij_java_doc_keep_invalid_tags = true 86 | ij_java_doc_param_description_on_new_line = false 87 | ij_java_doc_preserve_line_breaks = false 88 | ij_java_doc_use_throws_not_exception_tag = true 89 | ij_java_else_on_new_line = false 90 | ij_java_enum_constants_wrap = off 91 | ij_java_extends_keyword_wrap = off 92 | ij_java_extends_list_wrap = off 93 | ij_java_field_annotation_wrap = split_into_lines 94 | ij_java_field_name_prefix = 95 | ij_java_field_name_suffix = 96 | ij_java_finally_on_new_line = false 97 | ij_java_for_brace_force = always 98 | ij_java_for_statement_new_line_after_left_paren = false 99 | ij_java_for_statement_right_paren_on_new_line = false 100 | ij_java_for_statement_wrap = off 101 | ij_java_generate_final_locals = false 102 | ij_java_generate_final_parameters = false 103 | ij_java_if_brace_force = always 104 | ij_java_imports_layout = java.**,javax.**,|,*,|,$* 105 | ij_java_indent_case_from_switch = true 106 | ij_java_insert_inner_class_imports = false 107 | ij_java_insert_override_annotation = true 108 | ij_java_keep_blank_lines_before_right_brace = 0 109 | ij_java_keep_blank_lines_between_package_declaration_and_header = 1 110 | ij_java_keep_blank_lines_in_code = 1 111 | ij_java_keep_blank_lines_in_declarations = 1 112 | ij_java_keep_builder_methods_indents = false 113 | ij_java_keep_control_statement_in_one_line = true 114 | ij_java_keep_first_column_comment = true 115 | ij_java_keep_indents_on_empty_lines = false 116 | ij_java_keep_line_breaks = true 117 | ij_java_keep_multiple_expressions_in_one_line = false 118 | ij_java_keep_simple_blocks_in_one_line = true 119 | ij_java_keep_simple_classes_in_one_line = true 120 | ij_java_keep_simple_lambdas_in_one_line = true 121 | ij_java_keep_simple_methods_in_one_line = true 122 | ij_java_label_indent_absolute = false 123 | ij_java_label_indent_size = 0 124 | ij_java_lambda_brace_style = end_of_line 125 | ij_java_layout_static_imports_separately = true 126 | ij_java_line_comment_add_space = false 127 | ij_java_line_comment_add_space_on_reformat = false 128 | ij_java_line_comment_at_first_column = true 129 | ij_java_local_variable_name_prefix = 130 | ij_java_local_variable_name_suffix = 131 | ij_java_method_annotation_wrap = split_into_lines 132 | ij_java_method_brace_style = end_of_line 133 | ij_java_method_call_chain_wrap = off 134 | ij_java_method_parameters_new_line_after_left_paren = true 135 | ij_java_method_parameters_right_paren_on_new_line = true 136 | ij_java_method_parameters_wrap = off 137 | ij_java_modifier_list_wrap = false 138 | ij_java_multi_catch_types_wrap = split_into_lines 139 | ij_java_names_count_to_use_import_on_demand = 2147483647 140 | ij_java_new_line_after_lparen_in_annotation = false 141 | ij_java_new_line_after_lparen_in_deconstruction_pattern = true 142 | ij_java_new_line_after_lparen_in_record_header = false 143 | ij_java_packages_to_use_import_on_demand = 144 | ij_java_parameter_annotation_wrap = off 145 | ij_java_parameter_name_prefix = 146 | ij_java_parameter_name_suffix = 147 | ij_java_parentheses_expression_new_line_after_left_paren = false 148 | ij_java_parentheses_expression_right_paren_on_new_line = false 149 | ij_java_place_assignment_sign_on_next_line = false 150 | ij_java_prefer_longer_names = true 151 | ij_java_prefer_parameters_wrap = false 152 | ij_java_record_components_wrap = normal 153 | ij_java_repeat_synchronized = true 154 | ij_java_replace_instanceof_and_cast = false 155 | ij_java_replace_null_check = true 156 | ij_java_replace_sum_lambda_with_method_ref = true 157 | ij_java_resource_list_new_line_after_left_paren = false 158 | ij_java_resource_list_right_paren_on_new_line = false 159 | ij_java_resource_list_wrap = off 160 | ij_java_rparen_on_new_line_in_annotation = false 161 | ij_java_rparen_on_new_line_in_deconstruction_pattern = true 162 | ij_java_rparen_on_new_line_in_record_header = false 163 | ij_java_space_after_closing_angle_bracket_in_type_argument = false 164 | ij_java_space_after_colon = true 165 | ij_java_space_after_comma = true 166 | ij_java_space_after_comma_in_type_arguments = true 167 | ij_java_space_after_for_semicolon = true 168 | ij_java_space_after_quest = true 169 | ij_java_space_after_type_cast = true 170 | ij_java_space_before_annotation_array_initializer_left_brace = false 171 | ij_java_space_before_annotation_parameter_list = false 172 | ij_java_space_before_array_initializer_left_brace = true 173 | ij_java_space_before_catch_keyword = true 174 | ij_java_space_before_catch_left_brace = true 175 | ij_java_space_before_catch_parentheses = true 176 | ij_java_space_before_class_left_brace = true 177 | ij_java_space_before_colon = true 178 | ij_java_space_before_colon_in_foreach = true 179 | ij_java_space_before_comma = false 180 | ij_java_space_before_deconstruction_list = false 181 | ij_java_space_before_do_left_brace = true 182 | ij_java_space_before_else_keyword = true 183 | ij_java_space_before_else_left_brace = true 184 | ij_java_space_before_finally_keyword = true 185 | ij_java_space_before_finally_left_brace = true 186 | ij_java_space_before_for_left_brace = true 187 | ij_java_space_before_for_parentheses = true 188 | ij_java_space_before_for_semicolon = false 189 | ij_java_space_before_if_left_brace = true 190 | ij_java_space_before_if_parentheses = true 191 | ij_java_space_before_method_call_parentheses = false 192 | ij_java_space_before_method_left_brace = true 193 | ij_java_space_before_method_parentheses = false 194 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false 195 | ij_java_space_before_quest = true 196 | ij_java_space_before_switch_left_brace = true 197 | ij_java_space_before_switch_parentheses = true 198 | ij_java_space_before_synchronized_left_brace = true 199 | ij_java_space_before_synchronized_parentheses = true 200 | ij_java_space_before_try_left_brace = true 201 | ij_java_space_before_try_parentheses = true 202 | ij_java_space_before_type_parameter_list = false 203 | ij_java_space_before_while_keyword = true 204 | ij_java_space_before_while_left_brace = true 205 | ij_java_space_before_while_parentheses = true 206 | ij_java_space_inside_one_line_enum_braces = false 207 | ij_java_space_within_empty_array_initializer_braces = true 208 | ij_java_space_within_empty_method_call_parentheses = false 209 | ij_java_space_within_empty_method_parentheses = false 210 | ij_java_spaces_around_additive_operators = true 211 | ij_java_spaces_around_annotation_eq = true 212 | ij_java_spaces_around_assignment_operators = true 213 | ij_java_spaces_around_bitwise_operators = true 214 | ij_java_spaces_around_equality_operators = true 215 | ij_java_spaces_around_lambda_arrow = true 216 | ij_java_spaces_around_logical_operators = true 217 | ij_java_spaces_around_method_ref_dbl_colon = false 218 | ij_java_spaces_around_multiplicative_operators = true 219 | ij_java_spaces_around_relational_operators = true 220 | ij_java_spaces_around_shift_operators = true 221 | ij_java_spaces_around_type_bounds_in_type_parameters = true 222 | ij_java_spaces_around_unary_operator = false 223 | ij_java_spaces_within_angle_brackets = false 224 | ij_java_spaces_within_annotation_parentheses = false 225 | ij_java_spaces_within_array_initializer_braces = true 226 | ij_java_spaces_within_braces = true 227 | ij_java_spaces_within_brackets = false 228 | ij_java_spaces_within_cast_parentheses = false 229 | ij_java_spaces_within_catch_parentheses = false 230 | ij_java_spaces_within_deconstruction_list = false 231 | ij_java_spaces_within_for_parentheses = false 232 | ij_java_spaces_within_if_parentheses = false 233 | ij_java_spaces_within_method_call_parentheses = false 234 | ij_java_spaces_within_method_parentheses = false 235 | ij_java_spaces_within_parentheses = false 236 | ij_java_spaces_within_record_header = false 237 | ij_java_spaces_within_switch_parentheses = false 238 | ij_java_spaces_within_synchronized_parentheses = false 239 | ij_java_spaces_within_try_parentheses = false 240 | ij_java_spaces_within_while_parentheses = false 241 | ij_java_special_else_if_treatment = true 242 | ij_java_static_field_name_prefix = 243 | ij_java_static_field_name_suffix = 244 | ij_java_subclass_name_prefix = 245 | ij_java_subclass_name_suffix = Impl 246 | ij_java_ternary_operation_signs_on_next_line = false 247 | ij_java_ternary_operation_wrap = off 248 | ij_java_test_name_prefix = 249 | ij_java_test_name_suffix = Test 250 | ij_java_throws_keyword_wrap = off 251 | ij_java_throws_list_wrap = off 252 | ij_java_use_external_annotations = false 253 | ij_java_use_fq_class_names = false 254 | ij_java_use_relative_indents = false 255 | ij_java_use_single_class_imports = true 256 | ij_java_variable_annotation_wrap = off 257 | ij_java_visibility = public 258 | ij_java_while_brace_force = always 259 | ij_java_while_on_new_line = false 260 | ij_java_wrap_comments = false 261 | ij_java_wrap_first_method_in_call_chain = false 262 | ij_java_wrap_long_lines = false 263 | 264 | [{*.kt,*.kts}] 265 | indent_size = 4 266 | tab_width = 4 267 | ij_continuation_indent_size = 4 268 | ij_kotlin_align_in_columns_case_branch = false 269 | ij_kotlin_align_multiline_binary_operation = false 270 | ij_kotlin_align_multiline_extends_list = false 271 | ij_kotlin_align_multiline_method_parentheses = false 272 | ij_kotlin_align_multiline_parameters = true 273 | ij_kotlin_align_multiline_parameters_in_calls = false 274 | ij_kotlin_allow_trailing_comma = false 275 | ij_kotlin_allow_trailing_comma_on_call_site = false 276 | ij_kotlin_assignment_wrap = normal 277 | ij_kotlin_blank_lines_after_class_header = 1 278 | ij_kotlin_blank_lines_around_block_when_branches = 0 279 | ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1 280 | ij_kotlin_block_comment_add_space = false 281 | ij_kotlin_block_comment_at_first_column = true 282 | ij_kotlin_call_parameters_new_line_after_left_paren = true 283 | ij_kotlin_call_parameters_right_paren_on_new_line = true 284 | ij_kotlin_call_parameters_wrap = on_every_item 285 | ij_kotlin_catch_on_new_line = false 286 | ij_kotlin_class_annotation_wrap = split_into_lines 287 | ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL 288 | ij_kotlin_continuation_indent_for_chained_calls = false 289 | ij_kotlin_continuation_indent_for_expression_bodies = false 290 | ij_kotlin_continuation_indent_in_argument_lists = false 291 | ij_kotlin_continuation_indent_in_elvis = false 292 | ij_kotlin_continuation_indent_in_if_conditions = false 293 | ij_kotlin_continuation_indent_in_parameter_lists = false 294 | ij_kotlin_continuation_indent_in_supertype_lists = false 295 | ij_kotlin_else_on_new_line = false 296 | ij_kotlin_enum_constants_wrap = off 297 | ij_kotlin_extends_list_wrap = normal 298 | ij_kotlin_field_annotation_wrap = split_into_lines 299 | ij_kotlin_finally_on_new_line = false 300 | ij_kotlin_if_rparen_on_new_line = true 301 | ij_kotlin_import_nested_classes = false 302 | ij_kotlin_imports_layout = java.**,javax.**,*,kotlin.**,^ 303 | ij_kotlin_insert_whitespaces_in_simple_one_line_method = true 304 | ij_kotlin_keep_blank_lines_before_right_brace = 0 305 | ij_kotlin_keep_blank_lines_in_code = 1 306 | ij_kotlin_keep_blank_lines_in_declarations = 1 307 | ij_kotlin_keep_first_column_comment = true 308 | ij_kotlin_keep_indents_on_empty_lines = false 309 | ij_kotlin_keep_line_breaks = true 310 | ij_kotlin_lbrace_on_next_line = false 311 | ij_kotlin_line_break_after_multiline_when_entry = true 312 | ij_kotlin_line_comment_add_space = false 313 | ij_kotlin_line_comment_add_space_on_reformat = false 314 | ij_kotlin_line_comment_at_first_column = true 315 | ij_kotlin_method_annotation_wrap = split_into_lines 316 | ij_kotlin_method_call_chain_wrap = normal 317 | ij_kotlin_method_parameters_new_line_after_left_paren = true 318 | ij_kotlin_method_parameters_right_paren_on_new_line = true 319 | ij_kotlin_method_parameters_wrap = on_every_item 320 | ij_kotlin_name_count_to_use_star_import = 2147483647 321 | ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 322 | ij_kotlin_packages_to_use_import_on_demand = 323 | ij_kotlin_parameter_annotation_wrap = off 324 | ij_kotlin_space_after_comma = true 325 | ij_kotlin_space_after_extend_colon = true 326 | ij_kotlin_space_after_type_colon = true 327 | ij_kotlin_space_before_catch_parentheses = true 328 | ij_kotlin_space_before_comma = false 329 | ij_kotlin_space_before_extend_colon = true 330 | ij_kotlin_space_before_for_parentheses = true 331 | ij_kotlin_space_before_if_parentheses = true 332 | ij_kotlin_space_before_lambda_arrow = true 333 | ij_kotlin_space_before_type_colon = false 334 | ij_kotlin_space_before_when_parentheses = true 335 | ij_kotlin_space_before_while_parentheses = true 336 | ij_kotlin_spaces_around_additive_operators = true 337 | ij_kotlin_spaces_around_assignment_operators = true 338 | ij_kotlin_spaces_around_equality_operators = true 339 | ij_kotlin_spaces_around_function_type_arrow = true 340 | ij_kotlin_spaces_around_logical_operators = true 341 | ij_kotlin_spaces_around_multiplicative_operators = true 342 | ij_kotlin_spaces_around_range = false 343 | ij_kotlin_spaces_around_relational_operators = true 344 | ij_kotlin_spaces_around_unary_operator = false 345 | ij_kotlin_spaces_around_when_arrow = true 346 | ij_kotlin_variable_annotation_wrap = off 347 | ij_kotlin_while_on_new_line = false 348 | ij_kotlin_wrap_elvis_expressions = 1 349 | ij_kotlin_wrap_expression_body_functions = 1 350 | ij_kotlin_wrap_first_method_in_call_chain = false 351 | 352 | [{*.properties,spring.handlers,spring.schemas}] 353 | ij_properties_align_group_field_declarations = false 354 | ij_properties_keep_blank_lines = false 355 | ij_properties_key_value_delimiter = equals 356 | ij_properties_spaces_around_key_value_delimiter = false 357 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | **/build 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Gyuwon Yi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 스타트업 Architectural Runway 지침 2 | 3 | 나는 시리즈 B와 시리즈 C 전후 투자 단계에서 성장하는 복수의 스타트업에 참여하며 다음 단계로 나아가기 위해 마치 탈피의 고통처럼 겪는 기술 문제를 해결하기 위해 많은 기법들을 적용해 왔다. 이와 비슷한 상황에서 기술적 의사결정을 해야 하는 조직이 참고할 수 있도록 그동안의 경험 속에서 자주 반복되는 패턴들을 안내한다. 4 | 5 | 여기에 설명되는 다양한 모델은 정해진 답으로 제시된 것이 아니며 다양한 기술적 문제를 해결하는 과정의 한가지 힌트일 뿐이다. 모든 엔지니어는 각자가 마주하는 고유한 문제 상황에 대해 스스로 적절한 해결책을 선택할 책임이 있다. 6 | 7 | 만약 오류가 발견되거나 의문점이 생긴다면 누구나 issue를 등록하거나 pull request를 만들 수 있다. 8 | 9 | > [Mermaid](https://mermaid.js.org) 특성상 어두운 테마에서 그림의 가시성이 떨어질 수 있기 때문에 밝은 테마를 권장한다. 10 | 11 | ## 진화 단계의 시스템 아키텍처 모델 12 | 13 | 작은 실험과 빠른 프로토타이핑을 위해 설계된 시스템이 덩치가 커지고 요구사항 복잡도가 올라가고 기여하는 엔지니어 수가 늘어나면 기존의 아키텍처는 점점 다양한 비효율과 문제를 일으킨다. 다음 단계로 성장하기 위해 시스템은 보다 구조화되어 관리될 필요가 있다. 이런 상황을 마주하고 있는 팀이 고려해 볼 만한 설계 기법들을 조합한 아키텍처 모델을 설명한다. 14 | 15 | ### 고수준 아키텍처 모델 16 | 17 | 가장 높은 시점에서 전체 시스템은 다음 주요 영역으로 구분될 수 있다. 18 | 19 | - **UI:** 시스템은 시스템을 사용하는 다양한 고객군을 대상으로 UI 응용프로그램을 제공한다. 예를 들어 쇼핑 서비스의 경우 구매자를 위한 모바일 응용프로그램과 판매자를 위한 웹 응용프로그램을 제공할 수 있다. 20 | 21 | - **Domain Model:** 비즈니스 도메인의 문제를 해결하는 영역이다. 이 영역의 구성요소를 정의할 때 도메인 주도 설계의 Bounded Context는 좋은 도구다. Bounded Context를 기준으로 만들어진 구성요소는 정해진 맥락 하에서 담당한 문제에 집중할 수 있다. 22 | 23 | - **Gateways:** Domain Model 영역에서 구현된 문제 해법을 시스템 외부에 노출하는 역할을 담당한다. 게이트웨이 응용프로그램은 외부 시스템을 위해 API를 공개할 수도 있고 각 UI 응용프로그램에 특화된 기능을 담당할 수도 있다. 후자의 기능을 담당하는 게이트웨이를 BFF(Backend for Frontend)라고 부른다. 24 | 25 | - **Platform:** Domain Model 영역 구성요소를 지원하는 서비스가 구동된다. 26 | 27 | - **Legacy Systems:** 폐기 예정이거나 더이상 외부에 노출되지 않지만 Domain Model 영역 구현에 재활용되는 시스템이 이 영역에 위치한다. 28 | 29 | ```mermaid 30 | C4Context 31 | 32 | Enterprise_Boundary(wb, "System") { 33 | System_Boundary(uib, "UI") { 34 | Container(app1, "UI App 1") 35 | Container(app2, "UI App 2") 36 | } 37 | System_Boundary(sb, "Server") { 38 | System_Boundary(gwb, "Gateways") { 39 | Container(gw1, "API Gateway 1") 40 | Container(bff1, "BFF 1") 41 | Container(bff2, "BFF 2") 42 | } 43 | System_Boundary(dmb, "Domain Model") { 44 | Component(bc1, "Bounded Context 1") 45 | Component(bc2, "Bounded Context 2") 46 | Component(bc3, "Bounded Context 3") 47 | Component(bc4, "Bounded Context 4") 48 | } 49 | System_Boundary(pb, "Platform") { 50 | ContainerQueue(mh, "Message Hub") 51 | Container(fs1, "Platform Service 1") 52 | } 53 | } 54 | System_Boundary(lb, "Legacy Systems") { 55 | Container(ls1, "Legacy System 1") 56 | Container(ls2, "Legacy System 2") 57 | Container(ls3, "Legacy System 3") 58 | } 59 | } 60 | 61 | System(es1, "External System 1") 62 | System(es2, "External System 2") 63 | System(es3, "External System 3") 64 | 65 | UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="3") 66 | 67 | UpdateElementStyle(ls1, $bgColor="grey", $borderColor="grey") 68 | UpdateElementStyle(ls2, $bgColor="grey", $borderColor="grey") 69 | UpdateElementStyle(ls3, $bgColor="grey", $borderColor="grey") 70 | ``` 71 | 72 | #### BFF(Backend for Frontend) 73 | 74 | 특정 UI 응용프로그램을 지원할 목적으로 설계된 API 제공자이다. 핵심 논리는 Domain Model 영역에서 구현되기 때문에 BFF는 게이트웨이의 [Aggregation](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-aggregation), [Offloading](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-offloading), [Routing](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-routing) 기능을 담당한다. 75 | 76 | 참고자료 77 | 78 | - [Backends for Frontends pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/backends-for-frontends) 79 | - [Sam Newman - Backends For Frontends](https://samnewman.io/patterns/architectural/bff/) 80 | 81 | ```mermaid 82 | C4Context 83 | 84 | Enterprise_Boundary(wb, "System") { 85 | System_Boundary(uib, "UI") { 86 | Container(app1, "UI App 1") 87 | Container(app2, "UI App 2") 88 | } 89 | System_Boundary(sb, "Server") { 90 | System_Boundary(gwb, "Gateways") { 91 | Container(gw1, "API Gateway 1") 92 | Container(bff1, "BFF 1") 93 | Container(bff2, "BFF 2") 94 | } 95 | System_Boundary(dmb, "Domain Model") { 96 | Component(bc1, "Bounded Context 1") 97 | Component(bc2, "Bounded Context 2") 98 | Component(bc3, "Bounded Context 3") 99 | Component(bc4, "Bounded Context 4") 100 | } 101 | } 102 | } 103 | 104 | Rel(bff1, bc2, "Routes requests to", "HTTP") 105 | Rel(bff1, bc3, "Routes requests to", "HTTP") 106 | Rel(bff2, bc4, "Routes requests to", "HTTP") 107 | 108 | Rel(app1, bff1, "Makes API calls to", "HTTP") 109 | Rel(app2, bff2, "Makes API calls to", "HTTP") 110 | 111 | UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="3") 112 | 113 | UpdateElementStyle(gw1, $bgColor="lightgrey", $borderColor="grey") 114 | 115 | UpdateElementStyle(bc1, $bgColor="lightgrey", $borderColor="grey") 116 | ``` 117 | 118 | #### API 게이트웨이 119 | 120 | Domain Model 영역에서 구현된 기능을 외부 시스템에 제공하기 위해 공개 API를 제공한다. API 게이트웨이는 [Aggregation](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-aggregation), [Offloading](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-offloading), [Routing](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-routing) 기능을 담당한다. 121 | 122 | 참고자료 123 | 124 | - [API gateway pattern (microservices.io)](https://microservices.io/patterns/apigateway.html) 125 | 126 | ```mermaid 127 | C4Context 128 | 129 | Enterprise_Boundary(wb, "System") { 130 | System_Boundary(sb, "Server") { 131 | System_Boundary(gwb, "Gateways") { 132 | Container(gw1, "API Gateway 1") 133 | Container(bff1, "BFF 1") 134 | Container(bff2, "BFF 2") 135 | } 136 | System_Boundary(dmb, "Domain Model") { 137 | Component(bc1, "Bounded Context 1") 138 | Component(bc2, "Bounded Context 2") 139 | Component(bc3, "Bounded Context 3") 140 | Component(bc4, "Bounded Context 4") 141 | } 142 | } 143 | } 144 | System(es1, "External System 1") 145 | System(es2, "External System 2") 146 | 147 | Rel(gw1, bc2, "Routes requests to", "HTTP") 148 | Rel(gw1, bc3, "Routes requests to", "HTTP") 149 | Rel(gw1, bc4, "Routes requests to", "HTTP") 150 | 151 | Rel(es1, gw1, "Makes API calls to", "HTTP") 152 | Rel(es2, gw1, "Makes API calls to", "HTTP") 153 | 154 | UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="3") 155 | 156 | UpdateElementStyle(bff1, $bgColor="lightgrey", $borderColor="grey") 157 | UpdateElementStyle(bff2, $bgColor="lightgrey", $borderColor="grey") 158 | 159 | UpdateElementStyle(bc1, $bgColor="lightgrey", $borderColor="grey") 160 | ``` 161 | 162 | #### Bounded Context 간 의존성 163 | 164 | Bounded Context는 서로 협력해 더 큰 가치를 생산한다. 한 Bounded Context의 모델이 다른 Bounded Context의 모델에 의존할 때 직접 의존하지 않으며 메시지를 통해 정보를 주고받는 형태로 간접 의존한다. 메시지 교환은 추상화된 인터페이스를 통해 동기적으로 이뤄질 수도 있고 메시지 중개자를 통해 비동기적으로 이뤄질 수도 있다. 165 | 166 | ```mermaid 167 | C4Context 168 | 169 | Enterprise_Boundary(wb, "System") { 170 | System_Boundary(sb, "Server") { 171 | System_Boundary(dmb, "Domain Model") { 172 | Component(bc1, "Bounded Context 1") 173 | Component(bc2, "Bounded Context 2") 174 | Component(bc3, "Bounded Context 3") 175 | Component(bc4, "Bounded Context 4") 176 | } 177 | System_Boundary(pb, "Platform") { 178 | ContainerQueue(mh, "Message Hub") 179 | Container(fs1, "Platform Service 1") 180 | } 181 | } 182 | } 183 | 184 | Rel(bc1, bc2, "Sends messages to", "sync") 185 | Rel(bc3, bc2, "Sends messages to", "sync") 186 | Rel(bc3, bc4, "Sends messages to", "sync") 187 | 188 | Rel(bc1, mh, "Publishes messages to") 189 | Rel(bc3, mh, "Subscribes") 190 | 191 | UpdateRelStyle(bc1, bc2, $offsetY="-10", $offsetX="5") 192 | UpdateRelStyle(bc3, bc2, $offsetX="-100") 193 | UpdateRelStyle(bc1, mh, $offsetY="-15", $offsetX="-60") 194 | UpdateRelStyle(bc3, mh, $offsetY="-90", $offsetX="20") 195 | UpdateRelStyle(bc3, bc4, $offsetY="-10", $offsetX="5") 196 | 197 | UpdateLayoutConfig($c4ShapeInRow="1", $c4BoundaryInRow="3") 198 | 199 | UpdateElementStyle(fs1, $bgColor="lightgrey", $borderColor="grey") 200 | ``` 201 | 202 | 위 그림에서 Bounded Context 1은 Bounded Context 2에 동기적으로 메시지를 전달하는 반면, Bounded Context 1과 Bounded Context 3은 Message Hub를 통해 비동기적으로 메시지를 주고받으며 협력한다. 203 | 204 | #### 플랫폼 서비스 205 | 206 | 플랫폼 서비스는 여러 Bounded Context에 의해 재사용되는 기술 집약적 기능을 제공한다. 예를 들어 전역 범위의 유일성을 보장하는 엔터티 식별자를 생성하거나 암호화 기능을 제공할 수 있다. 207 | 208 | ```mermaid 209 | C4Context 210 | 211 | Enterprise_Boundary(wb, "System") { 212 | System_Boundary(sb, "Server") { 213 | System_Boundary(dmb, "Domain Model") { 214 | Component(bc1, "Bounded Context 1") 215 | Component(bc2, "Bounded Context 2") 216 | Component(bc3, "Bounded Context 3") 217 | Component(bc4, "Bounded Context 4") 218 | } 219 | System_Boundary(pb, "Platform") { 220 | ContainerQueue(mh, "Message Hub") 221 | Container(fs1, "Platform Service 1") 222 | } 223 | } 224 | } 225 | 226 | Rel(bc2, fs1, "Makes API calls to", "HTTP") 227 | Rel(bc3, fs1, "Makes API calls to", "HTTP") 228 | 229 | UpdateRelStyle(bc2, fs1, $offsetY="-30", $offsetX="-45") 230 | UpdateRelStyle(bc3, fs1, $offsetY="-35", $offsetX="-80") 231 | 232 | UpdateLayoutConfig($c4ShapeInRow="1", $c4BoundaryInRow="3") 233 | 234 | UpdateElementStyle(bc1, $bgColor="lightgrey", $borderColor="grey") 235 | UpdateElementStyle(bc4, $bgColor="lightgrey", $borderColor="grey") 236 | 237 | UpdateElementStyle(mh, $bgColor="lightgrey", $borderColor="grey") 238 | ``` 239 | 240 | #### 레거시 시스템 사용 241 | 242 | Bounded Context는 직접 비즈니스 도메인의 해법을 제공하는 것이 원칙이지만 현실적인 이유로 인해 해법 구현을 Legacy System에 위임할 수 있다. 예를 들어 Legacy System 자체는 폐기되었지만 포함된 기능 중 일부는 유용하고 재구현 비용이 비싸다면 Bounded Context의 인터페이스 설계에는 배제하되 구현에는 활용하는 것이 실용적일 수 있다. 243 | 244 | ```mermaid 245 | C4Context 246 | 247 | Enterprise_Boundary(wb, "System") { 248 | System_Boundary(sb, "Server") { 249 | System_Boundary(dmb, "Domain Model") { 250 | Component(bc1, "Bounded Context 1") 251 | Component(bc2, "Bounded Context 2") 252 | Component(bc3, "Bounded Context 3") 253 | Component(bc4, "Bounded Context 4") 254 | } 255 | } 256 | System_Boundary(lb, "Legacy Systems") { 257 | Container(ls1, "Legacy System 1") 258 | Container(ls2, "Legacy System 2") 259 | Container(ls3, "Legacy System 3") 260 | } 261 | } 262 | 263 | Rel(bc3, ls3, "Depends on", "HTTP") 264 | Rel(bc4, ls1, "Depends on", "HTTP") 265 | Rel(bc4, ls2, "Depends on", "HTTP") 266 | 267 | UpdateRelStyle(bc3, ls3, $offsetY="10", $offsetX="-30") 268 | UpdateRelStyle(bc4, ls1, $offsetY="-100", $offsetX="-40") 269 | UpdateRelStyle(bc4, ls2, $offsetY="-80", $offsetX="-40") 270 | 271 | UpdateLayoutConfig($c4ShapeInRow="1", $c4BoundaryInRow="3") 272 | 273 | UpdateElementStyle(bc1, $bgColor="lightgrey", $borderColor="grey") 274 | UpdateElementStyle(bc2, $bgColor="lightgrey", $borderColor="grey") 275 | ``` 276 | 277 | #### 외부 서비스 사용 278 | 279 | 시스템을 외부 시스템과 통합하는 방법으로 공개 API 제공할 수도 있지만 반대로 외부 시스템이 제공하는 API를 Bounded Context가 사용할 수도 있다. 이때 Bounded Context의 모델은 외부 시스템에 직접 의존하지 않으며 추상화된 인터페이스를 통해 간접 의존한다. 280 | 281 | ```mermaid 282 | C4Context 283 | 284 | Enterprise_Boundary(wb, "System") { 285 | System_Boundary(sb, "Server") { 286 | System_Boundary(dmb, "Domain Model") { 287 | Component(bc1, "Bounded Context 1") 288 | Component(bc2, "Bounded Context 2") 289 | Component(bc3, "Bounded Context 3") 290 | Component(bc4, "Bounded Context 4") 291 | } 292 | } 293 | } 294 | System(es3, "External System 3") 295 | 296 | Rel(bc2, es3, "Depends on", "HTTP") 297 | 298 | UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="3") 299 | 300 | UpdateElementStyle(bc1, $bgColor="lightgrey", $borderColor="grey") 301 | UpdateElementStyle(bc3, $bgColor="lightgrey", $borderColor="grey") 302 | UpdateElementStyle(bc4, $bgColor="lightgrey", $borderColor="grey") 303 | ``` 304 | 305 | ### Bounded Context 구현 306 | 307 | #### Bounded Context 구성 308 | 309 | **기본 구성** 310 | 311 | 일반적으로 Bounded Context는 하나의 모델을 포함한다. 이 경우 Bounded Context는 주요 구성요소 Contracts, Model, Adapters를 갖는다. 312 | 313 | ```mermaid 314 | C4Context 315 | 316 | Container_Boundary(bc1, "Bounded Context 1") { 317 | Component(a, "Adapters") 318 | Component(m, "Model") 319 | Component(c, "Contracts") 320 | } 321 | 322 | System_Boundary(cl, "Clients") { 323 | Container(ag, "API Gateway") 324 | Component(bc2, "Bounded Context 2") 325 | Component(ft, "Functional Test") 326 | } 327 | 328 | Component(el, "External Library") 329 | 330 | Rel(m, c, "Uses") 331 | Rel(a, m, "Implements") 332 | Rel(a, el, "Depends on") 333 | Rel(ag, c, "Depends on") 334 | Rel(bc2, c, "Depends on") 335 | Rel(ft, c, "Depends on") 336 | ``` 337 | 338 | - **Contracts:** Bounded Context가 외부에 노출하는 인터페이스 계약에 사용되는 메시지와 데이터를 정의한다. Bounded Context의 직접 또는 간접 클라이언트는 오직 Contracts에 정의된 코드에만 직접 의존할 수 있다. 339 | 340 | - **Model:** Bounded Context가 담당하는 도메인 모델이 표현된다. 문제 해법의 핵심 논리가 작성되며 전체 시스템 범위에서 가장 중요한 영역이다. 341 | 342 | - **Adapters:** Model이 순수한 코드로 문제 해법을 구현할 수 있도록 기반 코드를 제공한다. 예를 들어 Model에 데이터 영속 장치에 대한 추상화가 정의되었다면 이 추상화의 구현체를 Adapters가 제공한다. Model이 다른 시스템에 의존할 때 의존 대상의 설계에 의해 Model이 훼손되지 않도록 [변질 방지 계층(Anti-Corruption Layer, ACL)](https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer)이 포함될 수도 있다. 343 | 344 | Model과 Adapters의 역할과 관계를 이해하기 위해 주문을 조회하는 기능에 Repository 패턴이 사용된다고 생각해 보자. Model에 `OrderRepository` 인터페이스가 정의되고 `OrderQueryProcessor` 클래스가 `OrderRepository` 인터페이스에 의존해 `Order` 개체를 찾아 `OrderView` 개체로 변환해 반환하는 기능을 구현한다. Adapters는 외부 라이브러리가 제공하는 관계형 데이터베이스 드라이버를 사용해 `OrderRepository` 인터페이스를 구현한다. 이때 Model은 Adapters와 외부 라이브러리 어떤 것에도 의존하지 않기 때문에 적응력이 높아지고 테스트하기 쉬워진다. 345 | 346 | ```mermaid 347 | classDiagram 348 | 349 | namespace Model { 350 | class OrderQueryProcessor { 351 | + findOrder(id) OrderView? 352 | } 353 | class OrderRepository { 354 | <> 355 | + find(id) Order? 356 | } 357 | } 358 | namespace Adapters { 359 | class RelationalOrderRepository { 360 | + find(id) Order? 361 | } 362 | } 363 | namespace ExternalLibrary { 364 | class DatabaseDriver 365 | } 366 | 367 | OrderRepository <-- OrderQueryProcessor 368 | OrderRepository <|-- RelationalOrderRepository 369 | DatabaseDriver <-- RelationalOrderRepository 370 | ``` 371 | 372 | **CQRS(Command and Query Responsibility Segregation)** 373 | 374 | Bounded Context의 모델에 CQRS 패턴이 적용되면 단일 모델 구성의 Model이 Command Model과 Query Model로 분리된다. Command Model은 도메인 모델의 명령 처리를, Query Model은 도메인 모델의 조회 처리를 표현한다. 375 | 376 | ```mermaid 377 | C4Context 378 | 379 | Container_Boundary(bc, "Bounded Context") { 380 | Component(a, "Adapters") 381 | Container_Boundary(m, "Model") { 382 | Component(cm, "Command Model") 383 | Component(qm, "Query Model") 384 | } 385 | Component(c, "Contracts") 386 | } 387 | 388 | Component(el, "External Library") 389 | 390 | Rel(cm, c, "Uses") 391 | Rel(a, cm, "Implements") 392 | Rel(qm, c, "Uses") 393 | Rel(a, qm, "Implements") 394 | Rel(a, el, "Depends on") 395 | ``` 396 | 397 | #### Bounded Context 호스팅 398 | 399 | **단일 Bounded Context 호스팅** 400 | 401 | Bounded Context는 Microservices 패턴에서와 같이 각각 하나의 독립된 호스트 응용프로그램에서 독립적으로 구동될 수 있다. 특정 Bounded Context에 대해 독립적으로 자원을 할당하고 출시 주기를 관리하거나 다른 Bounded Context의 장애로 인한 효과를 차단해야 할 때 적합하다. 하지만 코드 수준의 격리에 비해 호스팅 환경을 격리하는 것은 많은 관리비용을 요구하기 때문에 규모가 작은 조직에서는 신중하게 접근하는 것이 좋다. 402 | 403 | ```mermaid 404 | C4Context 405 | 406 | Component(f, "API Framework") 407 | 408 | System_Boundary(dmb, "Domain Model") { 409 | Container(a, "API App") 410 | Component(bc, "Bounded Context") 411 | } 412 | 413 | Rel(a, f, "Depends on") 414 | Rel(a, bc, "Hosts") 415 | ``` 416 | 417 | **공유 호스트 응용프로그램** 418 | 419 | 여러 Bounded Context가 하나의 호스트 응용프로그램에서 구동될 수 있다. 공유된 호스트 응용프로그램에서 호스팅되는 Bounded Context들은 런타임, CPU, 메모리, 네트워크, 가용성 등을 공유한다. 420 | 421 | ```mermaid 422 | C4Context 423 | 424 | Component(f, "API Framework") 425 | 426 | System_Boundary(dmb, "Domain Model") { 427 | Component(bc1, "Bounded Context 1") 428 | Component(bc2, "Bounded Context 2") 429 | Component(bc3, "Bounded Context 3") 430 | Container(a, "API App") 431 | } 432 | 433 | UpdateLayoutConfig($c4ShapeInRow="3") 434 | 435 | Rel(a, f, "Depends on") 436 | Rel(a, bc1, "Hosts") 437 | Rel(a, bc2, "Hosts") 438 | Rel(a, bc3, "Hosts") 439 | ``` 440 | 441 | **다중 호스트 응용프로그램** 442 | 443 | 하나의 Bounded Context가 둘 이상의 응용프로그램에서 호스팅될 수 있다. 예를 들어 하나의 Bounded Context가 API 응용프로그램에서 호스팅되어 동기적으로 요청을 처리하며 동시에 메시지 처리 응용프로그램에서 호스팅되어 비동기적으로 요청을 처리하는 아키텍처도 가능하다. 444 | 445 | ```mermaid 446 | C4Context 447 | 448 | Deployment_Node(an, "API App Node") { 449 | Container(a, "API App") 450 | } 451 | 452 | Component(af, "API Framework") 453 | 454 | System_Boundary(dmb, "Domain Model") { 455 | Component(bc, "Bounded Context") 456 | } 457 | 458 | Deployment_Node(pn, "Processor App Node") { 459 | Container(p, "Processor App") 460 | } 461 | 462 | Component(pf, "Processor Framework") 463 | ContainerQueue(mh, "Message Hub") 464 | 465 | UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="3") 466 | 467 | Rel(a, af, "Depends on") 468 | Rel(a, bc, "Hosts") 469 | Rel(p, pf, "Depends on") 470 | Rel(p, bc, "Hosts") 471 | Rel(p, mh, "Subscribes") 472 | ``` 473 | ### 코드 디렉터리 474 | 475 | 시스템 아키텍처가 고려된 코드 디렉터리 구조는 아키텍처를 이해하고 관리하는 데 큰 도움이 된다. 예를 들어 쇼핑 서비스 시스템은 아키텍처를 반영해 코드 디렉터리를 아래와 같이 설계할 수 있다. 476 | 477 | ```text 478 | root/ 479 | ┣ server/ 480 | ┃ ┣ domain-model/ 481 | ┃ ┃ ┣ accounting/ 482 | ┃ ┃ ┃ ┣ adapters/ 483 | ┃ ┃ ┃ ┣ contracts/ 484 | ┃ ┃ ┃ ┣ model/ 485 | ┃ ┃ ┃ ┗ unit-tests/ 486 | ┃ ┃ ┣ identity/ 487 | ┃ ┃ ┃ ┣ adapters/ 488 | ┃ ┃ ┃ ┣ contracts/ 489 | ┃ ┃ ┃ ┣ model/ 490 | ┃ ┃ ┃ ┗ unit-tests/ 491 | ┃ ┃ ┣ inventory/ 492 | ┃ ┃ ┃ ┣ adapters/ 493 | ┃ ┃ ┃ ┣ contracts/ 494 | ┃ ┃ ┃ ┣ model/ 495 | ┃ ┃ ┃ ┗ unit-tests/ 496 | ┃ ┃ ┣ orders/ 497 | ┃ ┃ ┃ ┣ adapters/ 498 | ┃ ┃ ┃ ┣ api/ 499 | ┃ ┃ ┃ ┣ contracts/ 500 | ┃ ┃ ┃ ┣ infrastructure/ 501 | ┃ ┃ ┃ ┣ model/ 502 | ┃ ┃ ┃ ┣ processor/ 503 | ┃ ┃ ┃ ┗ unit-tests/ 504 | ┃ ┃ ┗ shared-hosts/ 505 | ┃ ┃ ┣ api/ 506 | ┃ ┃ ┣ infrastructure/ 507 | ┃ ┃ ┣ processor/ 508 | ┃ ┃ ┗ unit-tests/ 509 | ┃ ┣ gateways/ 510 | ┃ ┃ ┣ administration-bff/ 511 | ┃ ┃ ┃ ┣ api/ 512 | ┃ ┃ ┃ ┣ infrastructure/ 513 | ┃ ┃ ┃ ┗ unit-tests/ 514 | ┃ ┃ ┣ api-gateway/ 515 | ┃ ┃ ┃ ┣ api/ 516 | ┃ ┃ ┃ ┣ infrastructure/ 517 | ┃ ┃ ┃ ┗ unit-tests/ 518 | ┃ ┃ ┣ functional-tests/ 519 | ┃ ┃ ┣ seller-bff/ 520 | ┃ ┃ ┃ ┣ api/ 521 | ┃ ┃ ┃ ┣ infrastructure/ 522 | ┃ ┃ ┃ ┗ unit-tests/ 523 | ┃ ┃ ┗ shopper-bff/ 524 | ┃ ┃ ┣ api/ 525 | ┃ ┃ ┣ infrastructure/ 526 | ┃ ┃ ┗ unit-tests/ 527 | ┃ ┗ platform/ 528 | ┃ ┣ api/ 529 | ┃ ┣ infrastructure/ 530 | ┃ ┣ processor/ 531 | ┃ ┗ unit-tests/ 532 | ┗ ui/ 533 | ┣ administration-frontend/ 534 | ┃ ┗ web/ 535 | ┣ seller-frontend/ 536 | ┃ ┗ web/ 537 | ┗ shopper-frontend/ 538 | ┣ android/ 539 | ┣ ios/ 540 | ┗ web/ 541 | ``` 542 | 543 | #### `domain-model` 디렉터리 544 | 545 | Domain Model 영역 코드를 담는다. 546 | 547 | ```text 548 | domain-model/ 549 | ┣ accounting/ 550 | ┣ identity/ 551 | ┣ inventory/ 552 | ┣ orders/ 553 | ┃ ┣ api/ 554 | ┃ ┗ processor/ 555 | ┗ shared-hosts/ 556 | ┣ api/ 557 | ┗ processor/ 558 | ``` 559 | 560 | 사례에서 `domain-model` 디렉터리는 정산을 담당하는 Accounting Bounded Context, 계정 관리를 담당하는 Identity Bounded Context, 상품 인벤토리를 담당하는 Inventory Bounded Context, 주문 관리를 담당하는 Order Bounded Context의 코드를 담으며 여러 Bounded Context를 호스팅하는 공유 API 호스트 응용프로그램과 공유 메시지 처리 호스트 응용프로그램이 포함된다. Accounting Bounded Context와 Identity Bounded Context, Inventory Bounded Context는 공유 호스트 응용프로그램에서 호스팅되며 Order Bounded Context는 전용 호스트 응용프로그램이 사용된다. 561 | 562 | #### `infrastructure` 디렉터리 563 | 564 | 각 아키텍처 구성요소에 필요한 컴퓨팅 자원을 배치하는 IaC(Infrastructure as Code)를 담는다. 565 | 566 | ## 코드 스트림 567 | 568 | 소스 코드 형상의 변경 내역을 스트림으로 바라보는 모델은 여러 프로그래머가 협업할 때 필요한 관리비용을 줄여준다. 모든 팀 구성원이 코드에 담긴 지식에 쉽게 접근하고 분산된 작업 결과물을 안전하게 조립할 수 있다. 569 | 570 | ### 단일 저장소 전략 571 | 572 | 많은 개발 조직이 시스템 구성요소들을 수많은 개별 코드 저장소로 분해해 관리하며 이런 팀 중 대다수는 이것이 유일한 방법인 것으로 착각한다. 특히 서버 코드와 UI 클라이언트 코드를 하나의 저장소에 모아 두면 금새 종말이라도 찾아올 거라고들 상상한다. 하지만 그런 일은 전혀 일어나지 않는다. 잠깐만 사고실험을 해봐도 알 수 있다. 작은 조직은 코드 저장소를 하나로 통합할 때 오히려 이점이 크다. 또한 Google, Facebook 등 거대기업이 단일 저장소를 사용하는 사례도 찾기 어렵지 않다. 단일 저장소 전략은 최근에 등장한 개념이 아닌 오랫동안 사용되어 온 기법이다. 573 | 574 | 단일 저장소 전략의 장점은 다음과 같다. 575 | 576 | 1. **투명성:** 시스템의 모든 코드가 한 곳에 모여있기 때문에 비즈니스 프로세스 전체를 구현하는 코드를 누구나 빠르게 검색하고 이해할 수 있다. 개발자는 자신의 코드 수정이 야기할 여파를 예측하거나 다른 동료에 의해 작성된 의존 코드를 검사해 오류의 원인을 추적할 수 있게 된다. 예를 들어 자신의 개발 장비에서, 웹 프론트엔드 프로그래머는 서버 내부 구현을 조사할 수 있고 서버 프로그래머는 API 클라이언트의 사용 의도를 파악할 수 있다. 577 | 578 | 2. **변경 집합의 무결성:** 원자성을 갖는 코드 변경 집합에 비즈니스 가치 제공을 위한 코드 수정과 이 변경에 영향받는 코드의 대응을 함께 담을 수 있다. 개발팀은 변경 집합의 무결성을 통해 코드 스트림을 안전하게 보호할 수 있다. 579 | 580 | 3. **특정 시점의 전체 시스템 형상 확인:** 전체 시스템의 변경이 하나의 코드 스트림으로 표현되기 때문에 쉽 과거 특정 시점의 코드를 읽고 구동시켜 테스트할 수 있다. 581 | 582 | 4. **일관된 설계 개선:** 더 나은 설계 정책을 일관되게 적용하기 쉬워진다. 583 | 584 | 5. **표준화된 규칙 유지:** 권장되는 코드 작성과 코드 저장소 운영, 파이프라인 관리 등의 규칙을 시스템 전체 수준에서 표준화하고 관리할 수 있다. 이를 통해 관리비용이 절감되어 효율적으로 적정 수준의 코드 품질을 유지할 수 있다. 585 | 586 | ### Fast-Forward 스트림 587 | 588 | Git을 사용해 형상을 관리하는 전략은 다양하며 각 전략은 각자의 특징을 갖는다. 코드 스트림인 `main` 브랜치의 코드 변경을 다른 분기의 간섭이 없는 fast-forward 스트림으로 관리하면 코드의 변화가 극도로 단순해지기 때문에 코드의 역사를 이해하기 쉬워진다. 병합 지옥에서 벗어나고 코드 충돌 가능성이 줄어든다. 코드 형상으로 구현되는 시스템의 각 구성요소는 복잡한 의존성 버전 관리 없이 최신 상태를 유지할 수 있다. 새로운 합류한 팀원은 복잡한 브랜칭 전략을 배울 필요 없이 즉시 간단한 작업을 시작하고 완료 후 릴리스할 수 있다. 589 | 590 | 만약 코드를 쓰는 팀 내 모든 엔지니어가 작고 원자적이고 검증된 코드 변경에 숙련되어 [지속 통합(Continuous Integration)](https://www.notion.so/Continuous-Integration-6da6ddf312614b33a6d36236bb2fb40c?pvs=21) 또는 [Trunk-Based Development](https://trunkbaseddevelopment.com/)를 사용하기 되면 여러가지 효과를 얻을 수 있는데 그 중 하나로 fast-forward 코드 스트림 유지가 자연스러워진다. 591 | 592 | #### Fast-Forward 스트림 기반 협업 사례 593 | 594 | Fast-Forward 스트림을 사용하는 팀이 협업하는 가상의 사례를 설명한다. 이런 작업은 실제로는 프로그래머가 직접 처리하기 보다 GitHub의 ['Allow rebase merging'](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/configuring-commit-rebasing-for-pull-requests)과 같은 코드 저장소 관리 도구를 이용하는 것이 일반적이다. 595 | 596 | 1. `main` 브랜치에서 `work-1` 브랜치와 `work-2` 브랜치를 분기한다. 597 | 598 | ```mermaid 599 | gitGraph 600 | commit id: "1-M" 601 | commit id: "2-M" 602 | branch work-1 603 | branch work-2 604 | ``` 605 | 606 | 2. `main` 브랜치에 다른 경로에 의해 `3-M` 커밋이 추가된다. 607 | 608 | ```mermaid 609 | gitGraph 610 | commit id: "1-M" 611 | commit id: "2-M" 612 | branch work-1 613 | branch work-2 614 | checkout main 615 | commit id: "3-M" 616 | ``` 617 | 618 | 3. `work-1` 브랜치에 `4-W` 커밋과 `5-W` 커밋을 추가한다. 619 | 620 | ```mermaid 621 | gitGraph 622 | commit id: "1-M" 623 | commit id: "2-M" 624 | branch work-1 625 | branch work-2 626 | checkout main 627 | commit id: "3-M" 628 | checkout work-1 629 | commit id: "4-W" 630 | commit id: "5-W" 631 | ``` 632 | 633 | 4. `work-2` 브랜치에 `6-W` 커밋을 추가한다. 634 | 635 | ```mermaid 636 | gitGraph 637 | commit id: "1-M" 638 | commit id: "2-M" 639 | branch work-1 640 | branch work-2 641 | checkout main 642 | commit id: "3-M" 643 | checkout work-1 644 | commit id: "4-W" 645 | commit id: "5-W" 646 | checkout work-2 647 | commit id: "6-W" 648 | ``` 649 | 650 | 5. `work-1` 브랜치를 `main` 브랜치에 리베이스한다. 기존 `4-W` 커밋과 `5-W` 커밋은 각각 `4-M` 커밋과 `5-M` 커밋으로 교체된다. 651 | 652 | ```mermaid 653 | gitGraph 654 | commit id: "1-M" 655 | commit id: "2-M" 656 | branch work-2 657 | checkout main 658 | commit id: "3-M" 659 | branch work-1 660 | checkout work-1 661 | commit id: "4-M" 662 | commit id: "5-M" 663 | checkout work-2 664 | commit id: "6-W" 665 | ``` 666 | 667 | 6. `work-1` 브랜치를 `main` 브랜치에 fast-forward 병합한다. 668 | 669 | ```mermaid 670 | gitGraph 671 | commit id: "1-M" 672 | commit id: "2-M" 673 | branch work-2 674 | checkout main 675 | commit id: "3-M" 676 | commit id: "4-M" 677 | commit id: "5-M" 678 | branch work-1 679 | checkout work-2 680 | commit id: "6-W" 681 | ``` 682 | 683 | 7. `main` 브랜치에 병합된 `work-1` 브랜치를 삭제한다. 684 | 685 | ```mermaid 686 | gitGraph 687 | commit id: "1-M" 688 | commit id: "2-M" 689 | branch work-2 690 | checkout main 691 | commit id: "3-M" 692 | commit id: "4-M" 693 | commit id: "5-M" 694 | checkout work-2 695 | commit id: "6-W" 696 | ``` 697 | 698 | 8. `work-2` 브랜치를 `main` 브랜치에 리베이스한다. 기존 `6-W` 커밋은 `6-M` 커밋으로 교체된다. 699 | 700 | ```mermaid 701 | gitGraph 702 | commit id: "1-M" 703 | commit id: "2-M" 704 | commit id: "3-M" 705 | commit id: "4-M" 706 | commit id: "5-M" 707 | branch work-2 708 | checkout work-2 709 | commit id: "6-M" 710 | ``` 711 | 712 | 9. `work-2` 브랜치를 `main` 브랜치에 fast-forward 병합한다. 713 | 714 | ```mermaid 715 | gitGraph 716 | commit id: "1-M" 717 | commit id: "2-M" 718 | commit id: "3-M" 719 | commit id: "4-M" 720 | commit id: "5-M" 721 | commit id: "6-M" 722 | branch work-2 723 | ``` 724 | 725 | 10. `main` 브랜치에 병합된 `work-2` 브랜치를 삭제한다. 726 | 727 | ```mermaid 728 | gitGraph 729 | commit id: "1-M" 730 | commit id: "2-M" 731 | commit id: "3-M" 732 | commit id: "4-M" 733 | commit id: "5-M" 734 | commit id: "6-M" 735 | ``` 736 | 737 | ### 테스트 기법 738 | 739 | #### 단위 테스트(Unit Testing) 740 | 741 | 시스템을 구성하는 코드의 일부를 격리시켜 테스트한다. 단위 테스트는 작성 및 실행 비용이 싸고 테스트 결과가 안정적인 반면 성공적인 클라이언트 경험 설명력이 높지 않다. 742 | 743 | #### 기능 테스트(Functional Testing) 744 | 745 | 코드를 실행 가능한 형태로 빌드하고 실행환경에 배치한 후 공개된 인터페이스를 통해 클라이언트의 시각에서 테스트한다. 실행환경은 로컬 환경, 통합 환경, 테스트 환경, 스테이징 환경, 운영 환경 등이 있다. 기능 테스트는 작성 및 실행 비용이 비싸고 다양한 요인에 의해 테스트 오류([1종 오류](https://gyuwon.github.io/blog/2018/12/19/false-positive-in-software-testing.html)나 2종 오류)가 발생할 가능성이 존재하지만 성공적인 클라이언트 경험 설명력이 높다. 기능 테스트는 자동화 되거나(automated) 수동으로(manual) 실행된다. 746 | 747 | ### 지속 통합(Continuous Integration) 748 | 749 | 지속 통합은 팀의 모든 코더가 짧은 주기로 단일 통합 지점에 안전한 코드를 추가하는 기법이다. 프로그래머는 자신의 로컬 개발 장비에서 코드를 작성하고 동작을 검증한 후 Pull Request를 통해 동료의 리뷰와 테스트를 거쳐 단일 통합 지점인 코드 스트림에 코드를 추가한다. 750 | 751 | Martin Fowler는 [Continuous Integration Certification (martinfowler.com)](https://martinfowler.com/bliki/ContinuousIntegrationCertification.html) 에서 지속 통합 인증 기준으로 다음을 제시했다. 752 | 753 | 1. Every developer commits at least daily to the shared mainline 754 | 2. Every commit triggers an automated build and test 755 | 3. If build and test fails, it's repaired within ten minutes 756 | 757 | 참고자료 758 | 759 | - [It's not CI, it's just CI Theatre | GoCD Blog](https://www.gocd.org/2017/05/16/its-not-CI-its-CI-theatre.html) 760 | - [Continuous Integration (martinfowler.com)](https://martinfowler.com/articles/continuousIntegration.html) 761 | 762 | ```mermaid 763 | flowchart LR 764 | 765 | subgraph w[Working Copy] 766 | subgraph l[Local Environment] 767 | c[Code]-->lut[Unit Testing] 768 | lut-->laft[Automated Functional Testing] 769 | lut-->lmft[Manual Functional Testing] 770 | end 771 | end 772 | 773 | laft-->pr[Pull Request] 774 | lmft-->pr[Pull Request] 775 | 776 | subgraph i[Integration] 777 | pr-->ib[Build] 778 | ib-->iut[Unit Testing] 779 | pr-->cr[Code Review] 780 | iut-->id[Deploy] 781 | subgraph ie[Integration Environment] 782 | id-->ift[Automated Functional Testing] 783 | end 784 | cr-->prc[Complete] 785 | ift-->prc 786 | end 787 | 788 | prc-->m[Mainstream] 789 | ``` 790 | 791 | ### 파이프라인 792 | 793 | 작업 복사본 코드 형상이 코드 스트림에 병합되면, 즉 코드 스트림에 코드 변경이 추가되면, 변경 내용에 해당하는 조건을 가진 트리거가 파이프라인을 실행시킨다. 일반적으로 시스템의 각 구성요소는 디렉터리로 구분되기 때문에 디렉터리 경로 조건에 의해 트리거가 발동하는 경우가 많다. 각 파이프라인은 [지속 배치(Continuous Deployment)](https://www.notion.so/Continuous-Deployment-d114471c3b4e4ba68c703fbd6cd019e8?pvs=21) 또는 [지속 배달(Continuous Delivery)](https://www.notion.so/Continuous-Delivery-e05cc292b5984e83be3113840f0433a1?pvs=21) 과정을 담는다. 794 | 795 | ```mermaid 796 | flowchart LR 797 | 798 | subgraph c[Code] 799 | wc1[(Working Copy 1)]-->m[(Mainstream)] 800 | wc2[(Working Copy 2)]-->m 801 | wc3[(Working Copy 3)]-->m 802 | end 803 | 804 | subgraph p[Pipelines] 805 | subgraph app1p[App 1 Pipelines] 806 | app1ip[Infrastructure Pipeline] 807 | app1cp[Code Pipeline] 808 | end 809 | 810 | subgraph app2p[App 2 Pipelines] 811 | app2ip[Infrastructure Pipeline] 812 | app2cp[Code Pipeline] 813 | end 814 | 815 | style app1p stroke-dasharray: 5 5 816 | style app2p stroke-dasharray: 5 5 817 | end 818 | 819 | m--App 1 Infrastructure Trigger-->app1ip 820 | m--App 1 Code Trigger-->app1cp 821 | m--App 2 Infrastructure Trigger-->app2ip 822 | m--App 2 Code Trigger-->app2cp 823 | 824 | subgraph system[System] 825 | app1ip-->app1[App 1] 826 | app1cp-->app1 827 | 828 | app2ip-->app2[App 2] 829 | app2cp-->app2 830 | end 831 | ``` 832 | 833 | ### 지속 배치(Continuous Deployment) 834 | 835 | 지속 배치는 단일 통합 지점의 모든 변경을 자동으로 출시한다. 변경된 코드 형상은 빌드되어 아티팩트가 생성되고 아티팩트는 단위 테스트된 후 아티팩트 저장소에 저장된다. 테스트 환경, 스테이징 환경, 운영 환경에 배치되는 코드 형상은 다시 빌드되지 않으며 아티팩트 저장소에 저장된 아티팩트가 그대로 사용된다. 스테이징 환경은 내부에서만 접근할 수 있는 운영 환경의 일부다. 스테이징 환경과 운영 환경 배치에는 [Blue-Green 배치](https://docs.aws.amazon.com/whitepapers/latest/overview-deployment-options/bluegreen-deployments.html) 또는 [카나리 배치](https://wa.aws.amazon.com/wellarchitected/2020-07-02T19-33-23/wat.concept.canary-deployment.en.html) 등의 기법이 사용될 수 있다. 자동화된 테스트 만으로 높은 안정적 작동의 신뢰를 얻을 수 있는 API 응용프로그램에 자주 사용된다. 836 | 837 | ```mermaid 838 | flowchart LR 839 | 840 | i[Integration]-->pb[Build] 841 | 842 | as[(Artifact Store)] 843 | mut-.->|Artifact|as 844 | 845 | subgraph m[Mainstream] 846 | pb-->mut[Unit Testing] 847 | mut-->td[Deploy] 848 | subgraph t[Test Environment] 849 | td[Deploy]-->tft[Automated Function Testing] 850 | end 851 | subgraph p[Production Environment] 852 | subgraph s[Staging Environment] 853 | tft-->sd[Deploy] 854 | sd-->sft[Automated Functional Testing] 855 | end 856 | sft-->pd[Deploy] 857 | pd-->pft[Automated Functional Testing] 858 | end 859 | as-.->|Artifact|td 860 | as-.->|Artifact|sd 861 | as-.->|Artifact|pd 862 | end 863 | 864 | style s stroke-dasharray: 5 5 865 | ``` 866 | 867 | ### 지속 배달(Continuous Delivery) 868 | 869 | 지속 배달은 지속 배치와 아주 유사하다. 핵심 차이는 수동 승인 절차가 포함된다는 점이다. 지속 배달을 사용하면 단일 통합 지점의 모든 변경에 대해 배치 가능한 아티팩트가 준비되지만 이 아티팩트를 고객과의 접점에 배치할 지 여부는 개발팀의 정책에 의해 결정된다. 예를 들어 Product Owner나 QA 엔지니어가 출시를 결정할 수 있다. 자동화된 테스트 만으로는 높은 안정적 작동의 신뢰를 얻을 수 없거나 가치 전달 단위와 코드 변경 단위가 일치하지 않을 때 고객경험을 해칠 수 있는 UI 응용프로그램에 자주 사용된다. 수동 인증 절차는 테스트 환경이나 스테이징 환경, 또는 둘 다에 적용할 수 있다. 870 | 871 | ```mermaid 872 | flowchart LR 873 | 874 | i[Integration]-->pb[Build] 875 | 876 | as[(Artifact Store)] 877 | mut-.->|Artifact|as 878 | 879 | subgraph m[Mainstream] 880 | pb-->mut[Unit Testing] 881 | mut-->td[Deploy] 882 | subgraph t[Test Environment] 883 | td[Deploy]-->taft[Automated Function Testing] 884 | td-->tmft[Manual Functional Testing] 885 | taft-->ta[Approve] 886 | tmft-->ta 887 | end 888 | subgraph p[Production Environment] 889 | subgraph s[Staging Environment] 890 | direction LR 891 | ta-->sd[Deploy] 892 | sd-->saft[Automated Functional Testing] 893 | sd-->smft[Manual Functional Testing] 894 | saft-->sa[Approve] 895 | smft-->sa 896 | end 897 | sa-->pd[Deploy] 898 | pd-->paft[Automated Functional Testing] 899 | end 900 | as-.->|Artifact|td 901 | as-.->|Artifact|sd 902 | as-.->|Artifact|pd 903 | end 904 | 905 | style ta fill:#FF0,color:#000 906 | style sa fill:#FF0,color:#000 907 | style s stroke-dasharray: 5 5 908 | ``` 909 | -------------------------------------------------------------------------------- /spring-boot/domain-model/identity/adapters/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.springframework.boot.gradle.tasks.bundling.BootJar 2 | import org.springframework.boot.gradle.tasks.run.BootRun 3 | 4 | plugins { 5 | `java-library` 6 | id("org.springframework.boot") version "3.3.4" 7 | id("io.spring.dependency-management") version "1.1.6" 8 | } 9 | 10 | java { 11 | toolchain { 12 | languageVersion = JavaLanguageVersion.of(21) 13 | } 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | api(project(":domain-model:identity:model")) 22 | api("org.springframework.boot:spring-boot-starter-data-jpa") 23 | runtimeOnly("org.postgresql:postgresql") 24 | } 25 | 26 | tasks.named("bootJar") { 27 | enabled = false 28 | } 29 | 30 | tasks.named("bootRun") { 31 | enabled = false 32 | } 33 | -------------------------------------------------------------------------------- /spring-boot/domain-model/identity/api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | id("org.springframework.boot") version "3.3.4" 4 | id("io.spring.dependency-management") version "1.1.6" 5 | } 6 | 7 | java { 8 | toolchain { 9 | languageVersion = JavaLanguageVersion.of(21) 10 | } 11 | } 12 | 13 | repositories { 14 | mavenCentral() 15 | } 16 | 17 | dependencies { 18 | implementation(project(":domain-model:identity:model")) 19 | implementation(project(":domain-model:identity:adapters")) 20 | implementation("org.springframework.boot:spring-boot-starter-data-jpa") 21 | implementation("org.springframework.boot:spring-boot-starter-web") 22 | } 23 | -------------------------------------------------------------------------------- /spring-boot/domain-model/identity/api/src/main/java/commerce/identity/IdentityApiApp.java: -------------------------------------------------------------------------------- 1 | package commerce.identity; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class IdentityApiApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(IdentityApiApp.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot/domain-model/identity/api/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 31000 3 | 4 | spring: 5 | datasource: 6 | url: jdbc:postgresql://localhost:5432/commerce 7 | username: postgres 8 | password: mysecretpassword 9 | 10 | jpa: 11 | hibernate: 12 | ddl-auto: validate 13 | properties: 14 | hibernate: 15 | default_schema: identity 16 | generate-ddl: false 17 | open-in-view: false 18 | -------------------------------------------------------------------------------- /spring-boot/domain-model/identity/contracts/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | } 4 | 5 | java { 6 | toolchain { 7 | languageVersion = JavaLanguageVersion.of(21) 8 | } 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot/domain-model/identity/model/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | } 4 | 5 | java { 6 | toolchain { 7 | languageVersion = JavaLanguageVersion.of(21) 8 | } 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | api(project(":domain-model:identity:contracts")) 17 | } 18 | -------------------------------------------------------------------------------- /spring-boot/domain-model/identity/unit-tests/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.springframework.boot.gradle.tasks.bundling.BootJar 2 | 3 | plugins { 4 | java 5 | id("org.springframework.boot") version "3.3.4" 6 | id("io.spring.dependency-management") version "1.1.6" 7 | } 8 | 9 | java { 10 | toolchain { 11 | languageVersion = JavaLanguageVersion.of(21) 12 | } 13 | } 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | testImplementation(project(":domain-model:identity:model")) 21 | testImplementation(project(":domain-model:identity:adapters")) 22 | testImplementation(project(":domain-model:identity:api")) 23 | testImplementation("org.springframework.boot:spring-boot-starter-web") 24 | testImplementation("org.springframework.boot:spring-boot-starter-test") 25 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 26 | testRuntimeOnly("com.h2database:h2") 27 | } 28 | 29 | tasks.withType { 30 | useJUnitPlatform() 31 | } 32 | 33 | tasks.named("bootJar") { 34 | enabled = false 35 | } 36 | -------------------------------------------------------------------------------- /spring-boot/domain-model/identity/unit-tests/src/test/java/test/commerce/identity/IdentityApiApp_specs.java: -------------------------------------------------------------------------------- 1 | package test.commerce.identity; 2 | 3 | import commerce.identity.IdentityApiApp; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest( 8 | classes = IdentityApiApp.class, 9 | properties = { 10 | "spring.datasource.url=jdbc:h2:mem:testdb", 11 | "spring.jpa.hibernate.ddl-auto=create-drop" 12 | } 13 | ) 14 | public class IdentityApiApp_specs { 15 | 16 | @Test 17 | void 컨텍스트를_초기화한다() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot/domain-model/inventory/adapters/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.springframework.boot.gradle.tasks.bundling.BootJar 2 | import org.springframework.boot.gradle.tasks.run.BootRun 3 | 4 | plugins { 5 | `java-library` 6 | id("org.springframework.boot") version "3.3.4" 7 | id("io.spring.dependency-management") version "1.1.6" 8 | } 9 | 10 | java { 11 | toolchain { 12 | languageVersion = JavaLanguageVersion.of(21) 13 | } 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | api(project(":domain-model:inventory:model")) 22 | api("org.springframework.boot:spring-boot-starter-data-jpa") 23 | runtimeOnly("org.postgresql:postgresql") 24 | } 25 | 26 | tasks.named("bootJar") { 27 | enabled = false 28 | } 29 | 30 | tasks.named("bootRun") { 31 | enabled = false 32 | } 33 | 34 | tasks.named("jar") { 35 | archiveBaseName.set("inventory.adapters") 36 | } 37 | -------------------------------------------------------------------------------- /spring-boot/domain-model/inventory/contracts/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | } 4 | 5 | java { 6 | toolchain { 7 | languageVersion = JavaLanguageVersion.of(21) 8 | } 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | } 17 | 18 | tasks.named("jar") { 19 | archiveBaseName.set("inventory.contracts") 20 | } 21 | -------------------------------------------------------------------------------- /spring-boot/domain-model/inventory/model/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | } 4 | 5 | java { 6 | toolchain { 7 | languageVersion = JavaLanguageVersion.of(21) 8 | } 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | api(project(":domain-model:inventory:contracts")) 17 | } 18 | 19 | tasks.named("jar") { 20 | archiveBaseName.set("inventory.model") 21 | } 22 | -------------------------------------------------------------------------------- /spring-boot/domain-model/inventory/unit-tests/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.springframework.boot.gradle.tasks.bundling.BootJar 2 | 3 | plugins { 4 | java 5 | id("org.springframework.boot") version "3.3.4" 6 | id("io.spring.dependency-management") version "1.1.6" 7 | } 8 | 9 | java { 10 | toolchain { 11 | languageVersion = JavaLanguageVersion.of(21) 12 | } 13 | } 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | testImplementation(project(":domain-model:inventory:model")) 21 | testImplementation(project(":domain-model:inventory:adapters")) 22 | testImplementation("org.springframework.boot:spring-boot-starter-test") 23 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 24 | testRuntimeOnly("com.h2database:h2") 25 | } 26 | 27 | tasks.withType { 28 | useJUnitPlatform() 29 | } 30 | 31 | tasks.named("bootJar") { 32 | enabled = false 33 | } 34 | -------------------------------------------------------------------------------- /spring-boot/domain-model/shared-hosts/api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | id("org.springframework.boot") version "3.3.4" 4 | id("io.spring.dependency-management") version "1.1.6" 5 | } 6 | 7 | java { 8 | toolchain { 9 | languageVersion = JavaLanguageVersion.of(21) 10 | } 11 | } 12 | 13 | repositories { 14 | mavenCentral() 15 | } 16 | 17 | dependencies { 18 | implementation(project(":domain-model:inventory:model")) 19 | implementation(project(":domain-model:inventory:adapters")) 20 | implementation("org.springframework.boot:spring-boot-starter-web") 21 | } 22 | -------------------------------------------------------------------------------- /spring-boot/domain-model/shared-hosts/api/src/main/java/commerce/SharedApiApp.java: -------------------------------------------------------------------------------- 1 | package commerce; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SharedApiApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SharedApiApp.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot/domain-model/shared-hosts/api/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 30000 3 | 4 | spring: 5 | datasource: 6 | url: jdbc:postgresql://localhost:5432/commerce 7 | username: postgres 8 | password: mysecretpassword 9 | 10 | jpa: 11 | hibernate: 12 | ddl-auto: validate 13 | generate-ddl: false 14 | open-in-view: false 15 | -------------------------------------------------------------------------------- /spring-boot/domain-model/shared-hosts/unit-tests/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.springframework.boot.gradle.tasks.bundling.BootJar 2 | 3 | plugins { 4 | java 5 | id("org.springframework.boot") version "3.3.4" 6 | id("io.spring.dependency-management") version "1.1.6" 7 | } 8 | 9 | java { 10 | toolchain { 11 | languageVersion = JavaLanguageVersion.of(21) 12 | } 13 | } 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | testImplementation(project(":domain-model:inventory:model")) 21 | testImplementation(project(":domain-model:inventory:adapters")) 22 | testImplementation(project(":domain-model:shared-hosts:api")) 23 | testImplementation("org.springframework.boot:spring-boot-starter-web") 24 | testImplementation("org.springframework.boot:spring-boot-starter-test") 25 | testRuntimeOnly("org.junit.platform:junit-platform-launcher") 26 | testRuntimeOnly("com.h2database:h2") 27 | } 28 | 29 | tasks.withType { 30 | useJUnitPlatform() 31 | } 32 | 33 | tasks.named("bootJar") { 34 | enabled = false 35 | } 36 | -------------------------------------------------------------------------------- /spring-boot/domain-model/shared-hosts/unit-tests/src/test/java/test/commerce/SharedApiApp_specs.java: -------------------------------------------------------------------------------- 1 | package test.commerce; 2 | 3 | import commerce.SharedApiApp; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest( 8 | classes = SharedApiApp.class, 9 | properties = { 10 | "spring.datasource.url=jdbc:h2:mem:testdb", 11 | "spring.jpa.hibernate.ddl-auto=create-drop" 12 | } 13 | ) 14 | public class SharedApiApp_specs { 15 | 16 | @Test 17 | void 컨텍스트가_초기화된다() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot/gateways/administration-bff/api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | id("org.springframework.boot") version "3.3.4" 4 | id("io.spring.dependency-management") version "1.1.6" 5 | } 6 | 7 | java { 8 | toolchain { 9 | languageVersion = JavaLanguageVersion.of(21) 10 | } 11 | } 12 | 13 | extra["springCloudVersion"] = "2023.0.3" 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | implementation("org.springframework.cloud:spring-cloud-starter-gateway") 21 | } 22 | 23 | dependencyManagement { 24 | imports { 25 | mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot/gateways/administration-bff/api/src/main/java/commerce/administrationbff/AdministrationBffApp.java: -------------------------------------------------------------------------------- 1 | package commerce.administrationbff; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class AdministrationBffApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(AdministrationBffApp.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot/gateways/administration-bff/api/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 20002 3 | -------------------------------------------------------------------------------- /spring-boot/gateways/administration-bff/unit-tests/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.springframework.boot.gradle.tasks.bundling.BootJar 2 | 3 | plugins { 4 | java 5 | id("org.springframework.boot") version "3.3.4" 6 | id("io.spring.dependency-management") version "1.1.6" 7 | } 8 | 9 | java { 10 | toolchain { 11 | languageVersion = JavaLanguageVersion.of(21) 12 | } 13 | } 14 | 15 | extra["springCloudVersion"] = "2023.0.3" 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | testImplementation(project(":gateways:administration-bff:api")) 23 | testImplementation("org.springframework.cloud:spring-cloud-starter-gateway") 24 | testImplementation("org.springframework.boot:spring-boot-starter-test") 25 | } 26 | 27 | dependencyManagement { 28 | imports { 29 | mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}") 30 | } 31 | } 32 | 33 | tasks.test { 34 | useJUnitPlatform() 35 | } 36 | 37 | tasks.named("bootJar") { 38 | enabled = false 39 | } 40 | -------------------------------------------------------------------------------- /spring-boot/gateways/administration-bff/unit-tests/src/test/java/test/commerce/administrationbff/AdministrationBffApp_specs.java: -------------------------------------------------------------------------------- 1 | package test.commerce.administrationbff; 2 | 3 | import commerce.administrationbff.AdministrationBffApp; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest(classes = AdministrationBffApp.class) 8 | public class AdministrationBffApp_specs { 9 | 10 | @Test 11 | void 컨텍스트가_초기화된다() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot/gateways/seller-bff/api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | id("org.springframework.boot") version "3.3.4" 4 | id("io.spring.dependency-management") version "1.1.6" 5 | } 6 | 7 | java { 8 | toolchain { 9 | languageVersion = JavaLanguageVersion.of(21) 10 | } 11 | } 12 | 13 | extra["springCloudVersion"] = "2023.0.3" 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | implementation("org.springframework.cloud:spring-cloud-starter-gateway") 21 | } 22 | 23 | dependencyManagement { 24 | imports { 25 | mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot/gateways/seller-bff/api/src/main/java/commerce/sellerbff/SellerBffApp.java: -------------------------------------------------------------------------------- 1 | package commerce.sellerbff; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SellerBffApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SellerBffApp.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot/gateways/seller-bff/api/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 20000 3 | -------------------------------------------------------------------------------- /spring-boot/gateways/seller-bff/unit-tests/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.springframework.boot.gradle.tasks.bundling.BootJar 2 | 3 | plugins { 4 | java 5 | id("org.springframework.boot") version "3.3.4" 6 | id("io.spring.dependency-management") version "1.1.6" 7 | } 8 | 9 | java { 10 | toolchain { 11 | languageVersion = JavaLanguageVersion.of(21) 12 | } 13 | } 14 | 15 | extra["springCloudVersion"] = "2023.0.3" 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | testImplementation(project(":gateways:seller-bff:api")) 23 | testImplementation("org.springframework.cloud:spring-cloud-starter-gateway") 24 | testImplementation("org.springframework.boot:spring-boot-starter-test") 25 | } 26 | 27 | dependencyManagement { 28 | imports { 29 | mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}") 30 | } 31 | } 32 | 33 | tasks.test { 34 | useJUnitPlatform() 35 | } 36 | 37 | tasks.named("bootJar") { 38 | enabled = false 39 | } 40 | -------------------------------------------------------------------------------- /spring-boot/gateways/seller-bff/unit-tests/src/test/java/test/commerce/sellerbff/SellerBffApp_specs.java: -------------------------------------------------------------------------------- 1 | package test.commerce.sellerbff; 2 | 3 | import commerce.sellerbff.SellerBffApp; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest(classes = SellerBffApp.class) 8 | public class SellerBffApp_specs { 9 | 10 | @Test 11 | void 컨텍스트가_초기화된다() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot/gateways/shopper-bff/api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | id("org.springframework.boot") version "3.3.4" 4 | id("io.spring.dependency-management") version "1.1.6" 5 | } 6 | 7 | java { 8 | toolchain { 9 | languageVersion = JavaLanguageVersion.of(21) 10 | } 11 | } 12 | 13 | extra["springCloudVersion"] = "2023.0.3" 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | implementation("org.springframework.cloud:spring-cloud-starter-gateway") 21 | } 22 | 23 | dependencyManagement { 24 | imports { 25 | mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot/gateways/shopper-bff/api/src/main/java/commerce/shopperbff/ShopperBffApp.java: -------------------------------------------------------------------------------- 1 | package commerce.shopperbff; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ShopperBffApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ShopperBffApp.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot/gateways/shopper-bff/api/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 20001 3 | -------------------------------------------------------------------------------- /spring-boot/gateways/shopper-bff/unit-tests/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.springframework.boot.gradle.tasks.bundling.BootJar 2 | 3 | plugins { 4 | java 5 | id("org.springframework.boot") version "3.3.4" 6 | id("io.spring.dependency-management") version "1.1.6" 7 | } 8 | 9 | java { 10 | toolchain { 11 | languageVersion = JavaLanguageVersion.of(21) 12 | } 13 | } 14 | 15 | extra["springCloudVersion"] = "2023.0.3" 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | testImplementation(project(":gateways:shopper-bff:api")) 23 | testImplementation("org.springframework.cloud:spring-cloud-starter-gateway") 24 | testImplementation("org.springframework.boot:spring-boot-starter-test") 25 | } 26 | 27 | dependencyManagement { 28 | imports { 29 | mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}") 30 | } 31 | } 32 | 33 | tasks.test { 34 | useJUnitPlatform() 35 | } 36 | 37 | tasks.named("bootJar") { 38 | enabled = false 39 | } 40 | -------------------------------------------------------------------------------- /spring-boot/gateways/shopper-bff/unit-tests/src/test/java/test/commerce/shopperbff/ShopperBffApp_specs.java: -------------------------------------------------------------------------------- 1 | package test.commerce.shopperbff; 2 | 3 | import commerce.shopperbff.ShopperBffApp; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest(classes = ShopperBffApp.class) 8 | public class ShopperBffApp_specs { 9 | 10 | @Test 11 | void 컨텍스트가_초기화된다() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gyuwon/ArchitecturalRunwayGuidelines/e2a2840e7af9f19070d018c42f16eac0efb5df9d/spring-boot/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /spring-boot/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /spring-boot/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /spring-boot/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /spring-boot/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | include( 2 | "gateways:seller-bff:api", 3 | "gateways:seller-bff:unit-tests", 4 | "gateways:shopper-bff:api", 5 | "gateways:shopper-bff:unit-tests", 6 | "gateways:administration-bff:api", 7 | "gateways:administration-bff:unit-tests" 8 | ) 9 | 10 | include( 11 | "domain-model:identity:contracts", 12 | "domain-model:identity:model", 13 | "domain-model:identity:adapters", 14 | "domain-model:identity:api", 15 | "domain-model:identity:unit-tests" 16 | ) 17 | 18 | include( 19 | "domain-model:inventory:contracts", 20 | "domain-model:inventory:model", 21 | "domain-model:inventory:adapters", 22 | "domain-model:inventory:unit-tests" 23 | ) 24 | 25 | include( 26 | "domain-model:shared-hosts:api", 27 | "domain-model:shared-hosts:unit-tests" 28 | ) 29 | --------------------------------------------------------------------------------