Domain-Driven Design (DDD) es un enfoque de desarrollo de software que coloca el dominio de negocio en el centro. RAP (RESTful ABAP Programming) ofrece condiciones ideales para implementar principios DDD: Business Objects, Entities y Compositions reflejan directamente los conceptos DDD.
Por que DDD con RAP?
| Concepto DDD | Implementacion RAP | Ventaja |
|---|---|---|
| Bounded Context | Software Component / Package | Clara delimitacion de areas funcionales |
| Aggregate | Business Object (Root Entity) | Limites de consistencia definidos |
| Entity | CDS View Entity | Identidad y ciclo de vida |
| Value Object | CDS Structure / Embedded View | Objetos de valor inmutables |
| Domain Event | RAP Business Event | Acoplamiento debil entre contextos |
| Repository | RAP Runtime (EML) | Abstraccion de la persistencia |
Conceptos basicos de DDD
Bounded Context
Un Bounded Context es un limite logico dentro del cual aplica un modelo de dominio especifico. En ABAP Cloud, esto corresponde tipicamente a un Software Component o un arbol de paquetes.
┌─────────────────────────────────────────────────────────────┐│ ENTERPRISE ││ ┌─────────────────────┐ ┌─────────────────────────────┐ ││ │ SALES CONTEXT │ │ LOGISTICS CONTEXT │ ││ │ ┌───────────────┐ │ │ ┌───────────────────────┐ │ ││ │ │ Order (BO) │ │ │ │ Shipment (BO) │ │ ││ │ │ Customer │──┼────┼──│ DeliveryAddress │ │ ││ │ │ OrderItem │ │ │ │ ShipmentItem │ │ ││ │ └───────────────┘ │ │ └───────────────────────┘ │ ││ │ ┌───────────────┐ │ │ ┌───────────────────────┐ │ ││ │ │ Product (ref) │◄─┼────┼──│ Inventory (BO) │ │ ││ │ └───────────────┘ │ │ │ StockLevel │ │ ││ └─────────────────────┘ │ └───────────────────────┘ │ ││ └─────────────────────────────┘ │└─────────────────────────────────────────────────────────────┘Aggregate y Root Entity
Un Aggregate es un grupo de objetos relacionados con una Root Entity. En RAP, la Root Entity corresponde al Business Object con define root view entity:
" ROOT ENTITY = Aggregate Root@AbapCatalog.viewEnhancementCategory: [#NONE]@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Order - Root Entity (Aggregate)'
define root view entity ZI_Order as select from zorder composition [0..*] of ZI_OrderItem as _Items association [0..1] to ZI_Customer as _Customer on $projection.CustomerId = _Customer.CustomerId{ key order_id as OrderId, customer_id as CustomerId, order_date as OrderDate, @Semantics.amount.currencyCode: 'CurrencyCode' total_amount as TotalAmount, @Semantics.currencyCode: true currency_code as CurrencyCode, status as Status,
" Campos administrativos @Semantics.user.createdBy: true created_by as CreatedBy, @Semantics.systemDateTime.createdAt: true created_at as CreatedAt,
" Asociaciones _Items, _Customer}Child Entity (Parte del Aggregate)
Las Entities dentro de un Aggregate solo se alcanzan a traves de la Root Entity:
" CHILD ENTITY - Parte del Aggregate Order@AbapCatalog.viewEnhancementCategory: [#NONE]@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Order Item - Child Entity'
define view entity ZI_OrderItem as select from zorder_item association to parent ZI_Order as _Order on $projection.OrderId = _Order.OrderId association [1..1] to ZI_Product as _Product on $projection.ProductId = _Product.ProductId{ key order_id as OrderId, key item_id as ItemId, product_id as ProductId, quantity as Quantity, @Semantics.amount.currencyCode: 'CurrencyCode' unit_price as UnitPrice, @Semantics.currencyCode: true currency_code as CurrencyCode,
" Campo calculado quantity * unit_price as LineTotal,
_Order, _Product}Value Object
Los Value Objects no tienen identidad propia y se definen por sus atributos. En RAP se modelan como estructuras embebidas o campos calculados:
" Value Object como CDS Structure@EndUserText.label: 'Address Value Object'define structure zs_address { street : abap.char(60); house_number: abap.char(10); postal_code : abap.char(10); city : abap.char(40); country : land1;}
" Uso en Entitydefine root view entity ZI_Customer as select from zcustomer{ key customer_id as CustomerId, customer_name as CustomerName,
" Value Object: Direccion street as AddressStreet, house_number as AddressHouseNumber, postal_code as AddressPostalCode, city as AddressCity, country as AddressCountry}Implementar reglas de Aggregate en RAP
Regla 1: Acceso solo a traves del Aggregate Root
La Behavior Definition asegura que las Child Entities solo se manipulen a traves de la Root Entity:
managed implementation in class zbp_i_order unique;strict ( 2 );
define behavior for ZI_Order alias Orderpersistent table zorderlock masterauthorization master ( instance )etag master LastChangedAt{ create; update; delete;
" Items se crean/modifican a traves de Order association _Items { create; }
" Asegurar invariantes del Aggregate validation validateOrderConsistency on save { create; update; }
" Domain Events event OrderCreated; event OrderConfirmed;}
define behavior for ZI_OrderItem alias OrderItempersistent table zorder_itemlock dependent by _Orderauthorization dependent by _Order{ update; delete;
" Sin creacion directa - solo a traves de Order field ( readonly ) OrderId;
association _Order;}Regla 2: Proteger invariantes
Las invariantes del Aggregate se verifican en Validations:
CLASS lhc_order DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS validateOrderConsistency FOR VALIDATE ON SAVE IMPORTING keys FOR Order~validateOrderConsistency.ENDCLASS.
CLASS lhc_order IMPLEMENTATION. METHOD validateOrderConsistency. " Verificar invariantes del Aggregate READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
" Cargar Items READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order BY \_Items ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_items).
LOOP AT lt_orders INTO DATA(ls_order). " Invariante 1: Se requiere al menos un Item DATA(lt_order_items) = FILTER #( lt_items WHERE OrderId = ls_order-OrderId ).
IF lines( lt_order_items ) = 0. APPEND VALUE #( %tky = ls_order-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'El pedido debe tener al menos una posicion' ) ) TO reported-order. APPEND VALUE #( %tky = ls_order-%tky ) TO failed-order. CONTINUE. ENDIF.
" Invariante 2: Total debe coincidir con Items DATA(lv_calculated_total) = REDUCE decfloat34( INIT sum = CONV decfloat34( 0 ) FOR item IN lt_order_items NEXT sum = sum + ( item-Quantity * item-UnitPrice ) ).
IF ls_order-TotalAmount <> lv_calculated_total. APPEND VALUE #( %tky = ls_order-%tky %element-TotalAmount = if_abap_behv=>mk-on %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'El total no coincide con las posiciones' ) ) TO reported-order. APPEND VALUE #( %tky = ls_order-%tky ) TO failed-order. ENDIF. ENDLOOP. ENDMETHOD.ENDCLASS.Regla 3: Consistencia transaccional
Un Aggregate siempre se guarda como un todo:
METHOD create_order_with_items. " Deep Insert - Crear Aggregate como un todo MODIFY ENTITIES OF zi_order ENTITY Order CREATE FIELDS ( CustomerId OrderDate TotalAmount CurrencyCode Status ) WITH VALUE #( ( %cid = 'ORDER_1' CustomerId = iv_customer_id OrderDate = cl_abap_context_info=>get_system_date( ) TotalAmount = lv_total CurrencyCode = 'EUR' Status = 'NEW' ) )
CREATE BY \_Items FIELDS ( ProductId Quantity UnitPrice CurrencyCode ) WITH VALUE #( ( %cid_ref = 'ORDER_1' %target = VALUE #( ( %cid = 'ITEM_1' ProductId = 'PROD001' Quantity = 2 UnitPrice = '100.00' CurrencyCode = 'EUR' ) ( %cid = 'ITEM_2' ProductId = 'PROD002' Quantity = 1 UnitPrice = '250.00' CurrencyCode = 'EUR' ) ) ) ) MAPPED DATA(lt_mapped) FAILED DATA(lt_failed) REPORTED DATA(lt_reported).
" Todo o nada - transaccional IF lt_failed IS NOT INITIAL. " Rollback automatico por RAP Runtime RAISE EXCEPTION TYPE cx_order_creation_failed. ENDIF.
COMMIT ENTITIES.ENDMETHOD.Implementar Domain Services
Los Domain Services encapsulan logica de negocio que no pertenece a una sola Entity:
" Interfaz para Domain ServiceINTERFACE zif_pricing_service. METHODS calculate_order_total IMPORTING it_items TYPE ztt_order_items iv_customer_tier TYPE zdd_customer_tier RETURNING VALUE(rs_result) TYPE zs_pricing_result.
METHODS apply_discount IMPORTING iv_subtotal TYPE decfloat34 iv_discount_code TYPE zdd_discount_code RETURNING VALUE(rv_total) TYPE decfloat34.ENDINTERFACE.
" ImplementacionCLASS zcl_pricing_service DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES zif_pricing_service.
PRIVATE SECTION. METHODS get_customer_discount IMPORTING iv_tier TYPE zdd_customer_tier RETURNING VALUE(rv_discount) TYPE decfloat34.ENDCLASS.
CLASS zcl_pricing_service IMPLEMENTATION. METHOD zif_pricing_service~calculate_order_total. " Calcular subtotal DATA(lv_subtotal) = REDUCE decfloat34( INIT sum = CONV decfloat34( 0 ) FOR item IN it_items NEXT sum = sum + ( item-quantity * item-unit_price ) ).
" Aplicar descuento por cliente DATA(lv_discount) = get_customer_discount( iv_customer_tier ). DATA(lv_discount_amount) = lv_subtotal * lv_discount / 100.
rs_result-subtotal = lv_subtotal. rs_result-discount_percentage = lv_discount. rs_result-discount_amount = lv_discount_amount. rs_result-total = lv_subtotal - lv_discount_amount. ENDMETHOD.
METHOD zif_pricing_service~apply_discount. " Validar y aplicar codigo de descuento SELECT SINGLE discount_percentage FROM zdiscount_codes WHERE code = @iv_discount_code AND valid_from <= @cl_abap_context_info=>get_system_date( ) AND valid_to >= @cl_abap_context_info=>get_system_date( ) INTO @DATA(lv_percentage).
IF sy-subrc = 0. rv_total = iv_subtotal * ( 1 - lv_percentage / 100 ). ELSE. rv_total = iv_subtotal. ENDIF. ENDMETHOD.
METHOD get_customer_discount. CASE iv_tier. WHEN 'GOLD'. rv_discount = 15. WHEN 'SILVER'. rv_discount = 10. WHEN 'BRONZE'. rv_discount = 5. WHEN OTHERS. rv_discount = 0. ENDCASE. ENDMETHOD.ENDCLASS.Usar Domain Service en RAP Action
CLASS lhc_order DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS recalculateTotal FOR MODIFY IMPORTING keys FOR ACTION Order~recalculateTotal RESULT result.ENDCLASS.
CLASS lhc_order IMPLEMENTATION. METHOD recalculateTotal. " Cargar Order e Items READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order BY \_Items ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_items).
READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order BY \_Customer FIELDS ( CustomerTier ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_customers).
" Instanciar Domain Service DATA(lo_pricing) = NEW zcl_pricing_service( ).
LOOP AT lt_orders INTO DATA(ls_order). " Obtener Items y datos de cliente DATA(lt_order_items) = FILTER #( lt_items WHERE OrderId = ls_order-OrderId ). DATA(ls_customer) = VALUE #( lt_customers[ CustomerId = ls_order-CustomerId ] OPTIONAL ).
" Llamar Domain Service DATA(ls_pricing) = lo_pricing->zif_pricing_service~calculate_order_total( it_items = CORRESPONDING #( lt_order_items ) iv_customer_tier = ls_customer-CustomerTier ).
" Actualizar Aggregate MODIFY ENTITIES OF zi_order IN LOCAL MODE ENTITY Order UPDATE FIELDS ( TotalAmount DiscountAmount ) WITH VALUE #( ( %tky = ls_order-%tky TotalAmount = ls_pricing-total DiscountAmount = ls_pricing-discount_amount ) ). ENDLOOP.
" Retornar resultado READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT result. ENDMETHOD.ENDCLASS.Domain Events en RAP
Los Domain Events senalan eventos de negocio importantes y permiten acoplamiento debil entre Bounded Contexts:
Definir Events
" Behavior Definition con Eventsmanaged implementation in class zbp_i_order unique;
define behavior for ZI_Order alias Order{ " ... Operaciones estandar ...
" Declarar Domain Events event OrderCreated parameter zs_order_created_event; event OrderConfirmed parameter zs_order_confirmed_event; event OrderShipped parameter zs_order_shipped_event; event OrderCancelled;}Event Parameters como estructura
" Definicion de Event Payload@EndUserText.label: 'Order Created Event'define abstract entity ZA_OrderCreatedEvent{ OrderId : abap.numc(10); CustomerId : abap.numc(10); OrderDate : abap.dats; TotalAmount : abap.dec(15,2); CurrencyCode : abap.cuky; ItemCount : abap.int4;}Disparar Events
CLASS lhc_order IMPLEMENTATION. METHOD confirmOrder. " Cambiar status MODIFY ENTITIES OF zi_order IN LOCAL MODE ENTITY Order UPDATE FIELDS ( Status ConfirmedAt ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky Status = 'CONFIRMED' ConfirmedAt = utclong_current( ) ) ) FAILED failed REPORTED reported.
" Leer datos actuales READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
" Disparar Domain Event LOOP AT lt_orders INTO DATA(ls_order). RAISE ENTITY EVENT zi_order~OrderConfirmed FROM VALUE #( ( %key = ls_order-%key %param = VALUE zs_order_confirmed_event( order_id = ls_order-OrderId customer_id = ls_order-CustomerId confirmed_at = ls_order-ConfirmedAt total_amount = ls_order-TotalAmount currency_code = ls_order-CurrencyCode ) ) ). ENDLOOP.
" Resultado result = CORRESPONDING #( lt_orders ). ENDMETHOD.ENDCLASS.Event Handler en otro Bounded Context
CLASS zcl_logistics_event_handler DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_rap_event_handler.ENDCLASS.
CLASS zcl_logistics_event_handler IMPLEMENTATION. METHOD if_rap_event_handler~handle_event. CASE iv_event_name. WHEN 'ORDERCONFIRMED'. " Leer datos del Event DATA ls_event TYPE zs_order_confirmed_event. ls_event = CORRESPONDING #( is_event_data ).
" Crear Shipment en Logistics Context MODIFY ENTITIES OF zi_shipment ENTITY Shipment CREATE FIELDS ( OrderId CustomerId Status ) WITH VALUE #( ( %cid = 'SHIP_1' OrderId = ls_event-order_id CustomerId = ls_event-customer_id Status = 'PENDING' ) ) MAPPED DATA(lt_mapped) FAILED DATA(lt_failed) REPORTED DATA(lt_reported).
IF lt_failed IS INITIAL. COMMIT ENTITIES. ENDIF. ENDCASE. ENDMETHOD.ENDCLASS.Ubiquitous Language
Un concepto central de DDD es el Ubiquitous Language - un vocabulario comun para el area funcional y desarrollo:
CDS Naming Conventions
" Nombres de Entity corresponden a terminos de negociodefine root view entity ZI_SalesOrder " Pedido de clientedefine view entity ZI_SalesOrderItem " Posicion de pedidodefine view entity ZI_DeliveryNote " Nota de entregadefine view entity ZI_Invoice " Facturadefine root view entity ZI_Customer " Cliente
" Nombres de campos son autoexplicativos@EndUserText.label: 'Pedido de cliente'define root view entity ZI_SalesOrder{ key order_number as OrderNumber, " Numero de pedido customer_number as CustomerNumber, " Numero de cliente order_date as OrderDate, " Fecha de pedido requested_delivery as RequestedDelivery, " Fecha de entrega deseada net_value as NetValue, " Valor neto tax_amount as TaxAmount, " Importe de impuestos gross_value as GrossValue, " Valor bruto payment_terms as PaymentTerms, " Condiciones de pago shipping_method as ShippingMethod, " Metodo de envio order_status as OrderStatus " Estado del pedido}Tipos de datos especificos del dominio
" Elementos de datos propios para terminos de negocio@EndUserText.label: 'Numero de pedido'@AbapCatalog.dataMaintenance: #ALLOWEDdefine type zdd_order_number : abap.numc(10);
@EndUserText.label: 'Numero de cliente'define type zdd_customer_number : abap.numc(10);
@EndUserText.label: 'Estado del pedido'define type zdd_order_status : abap.char(2);
" Uso en tabla y CDSdefine table zsales_order { key client : abap.clnt not null; key order_number : zdd_order_number not null; customer_number : zdd_customer_number; order_status : zdd_order_status;}Integracion de Bounded Context
Anti-Corruption Layer
Un Anti-Corruption Layer protege el propio Bounded Context de modelos de datos externos:
" Interfaz al sistema externoINTERFACE zif_external_product_service. METHODS get_product IMPORTING iv_external_id TYPE string RETURNING VALUE(rs_product) TYPE zs_external_product.ENDINTERFACE.
" Anti-Corruption LayerCLASS zcl_product_acl DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. METHODS constructor IMPORTING io_external_service TYPE REF TO zif_external_product_service.
METHODS get_product_for_order IMPORTING iv_product_id TYPE zdd_product_id RETURNING VALUE(rs_product) TYPE zs_order_product RAISING cx_product_not_found.
PRIVATE SECTION. DATA mo_external_service TYPE REF TO zif_external_product_service.
METHODS translate_product IMPORTING is_external TYPE zs_external_product RETURNING VALUE(rs_internal) TYPE zs_order_product.ENDCLASS.
CLASS zcl_product_acl IMPLEMENTATION. METHOD constructor. mo_external_service = io_external_service. ENDMETHOD.
METHOD get_product_for_order. " Obtener producto externo DATA(ls_external) = mo_external_service->get_product( CONV #( iv_product_id ) ).
IF ls_external IS INITIAL. RAISE EXCEPTION TYPE cx_product_not_found. ENDIF.
" Traducir a modelo de dominio propio rs_product = translate_product( ls_external ). ENDMETHOD.
METHOD translate_product. " Mapeo a estructura propia rs_internal-product_id = is_external-sku. rs_internal-name = is_external-title. rs_internal-price = is_external-list_price. rs_internal-currency = is_external-currency_iso.
" Mapear categorias externas a internas CASE is_external-category_code. WHEN 'ELEC'. rs_internal-product_group = 'ELECTRONICS'. WHEN 'FURN'. rs_internal-product_group = 'FURNITURE'. WHEN OTHERS. rs_internal-product_group = 'OTHER'. ENDCASE. ENDMETHOD.ENDCLASS.Context Map
Un Context Map documenta las relaciones entre Bounded Contexts:
┌─────────────────────────────────────────────────────────────────┐│ CONTEXT MAP │├─────────────────────────────────────────────────────────────────┤│ ││ ┌──────────────┐ Events ┌──────────────────┐ ││ │ SALES │ ──────────────────────► │ LOGISTICS │ ││ │ Context │ OrderConfirmed │ Context │ ││ │ │ OrderCancelled │ │ ││ │ [Upstream] │ │ [Downstream] │ ││ └──────────────┘ └──────────────────┘ ││ │ │ ││ │ API Call │ ││ ▼ │ ││ ┌──────────────┐ │ ││ │ PRODUCT │ ◄────────────────────────────────┘ ││ │ Context │ ACL (Read-Only) ││ │ │ ││ │ [Upstream] │ ││ └──────────────┘ ││ │ ││ │ Conformist ││ ▼ ││ ┌──────────────┐ ││ │ EXTERNAL │ ││ │ Catalog │ ││ │ (SAP S/4) │ ││ └──────────────┘ │└─────────────────────────────────────────────────────────────────┘Mejores practicas para DDD con RAP
1. Establecer limites de Aggregate correctamente
" BIEN: Order es Aggregate Root con Items como Childrendefine root view entity ZI_Order composition [0..*] of ZI_OrderItem as _Items " Parte del Aggregate
" MAL: Aggregate demasiado grandedefine root view entity ZI_Order composition [0..*] of ZI_OrderItem as _Items composition [0..*] of ZI_Invoice as _Invoices " Aggregate propio! composition [0..*] of ZI_Shipment as _Shipments " Aggregate propio!2. Consistencia dentro del Aggregate
" Determination actualiza valores derivados en el Aggregatedetermination calculateTotals on modify { field Quantity, UnitPrice; " Al cambiar Items}
METHOD calculateTotals. " Sumar todos los Items DATA(lv_total) = REDUCE decfloat34( INIT sum = CONV decfloat34( 0 ) FOR item IN lt_items NEXT sum = sum + item-LineTotal ).
" Actualizar Root Entity MODIFY ENTITIES OF zi_order IN LOCAL MODE ENTITY Order UPDATE FIELDS ( TotalAmount ) WITH VALUE #( ( %tky = ls_order-%tky TotalAmount = lv_total ) ).ENDMETHOD.3. Abstraer Repositories
" Interfaz de Repository para AggregateINTERFACE zif_order_repository. METHODS find_by_id IMPORTING iv_order_id TYPE zdd_order_id RETURNING VALUE(rs_order) TYPE zs_order_aggregate RAISING cx_not_found.
METHODS find_by_customer IMPORTING iv_customer_id TYPE zdd_customer_id RETURNING VALUE(rt_orders) TYPE ztt_order_aggregates.
METHODS save IMPORTING is_order TYPE zs_order_aggregate RAISING cx_save_failed.ENDINTERFACE.
" Implementacion basada en RAPCLASS zcl_order_repository DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES zif_order_repository.ENDCLASS.
CLASS zcl_order_repository IMPLEMENTATION. METHOD zif_order_repository~find_by_id. READ ENTITIES OF zi_order ENTITY Order ALL FIELDS WITH VALUE #( ( OrderId = iv_order_id ) ) RESULT DATA(lt_orders) FAILED DATA(lt_failed).
IF lt_failed IS NOT INITIAL OR lt_orders IS INITIAL. RAISE EXCEPTION TYPE cx_not_found. ENDIF.
" Cargar Items READ ENTITIES OF zi_order ENTITY Order BY \_Items ALL FIELDS WITH VALUE #( ( OrderId = iv_order_id ) ) RESULT DATA(lt_items).
" Componer Aggregate rs_order-header = CORRESPONDING #( lt_orders[ 1 ] ). rs_order-items = CORRESPONDING #( lt_items ). ENDMETHOD.ENDCLASS.4. Metodos de negocio en lugar de tecnicos
" BIEN: Action de negocioaction confirmOrder result [1] $self;action shipOrder parameter ZA_ShipmentDetails result [1] $self;action cancelOrder parameter ZA_CancellationReason result [1] $self;
" MAL: Action tecnicaaction setStatusConfirmed result [1] $self;action updateShippingData parameter ZA_ShippingData result [1] $self;Temas relacionados
- RAP Basics - Base para desarrollo de Business Objects
- Design Patterns para RAP - Factory, Strategy y mas
- RAP Business Events - Event-Driven Architecture
- Clean ABAP - Calidad de codigo y convenciones de nomenclatura
Conclusion
Domain-Driven Design y RAP se complementan excelentemente:
- Aggregate = Business Object: La arquitectura RAP con Root y Child Entities corresponde directamente al concepto de Aggregate
- Bounded Contexts = Software Components: Las estructuras de paquetes definen limites claros
- Domain Events = RAP Business Events: Acoplamiento debil entre contextos
- Repository = EML/RAP Runtime: La persistencia es abstraida por el framework
DDD requiere mas analisis inicial, pero se compensa con mejor mantenibilidad, comunicacion mas clara entre negocio y desarrollo, y arquitectura mas flexible. RAP proporciona la base tecnica para implementar estos conceptos elegantemente.