Учитывая архитектуру SoC, в которой существуют следующие классы:

  • InvoicesService  – Сервисный уровень –
  • Счета -Доменный уровень-

Скажем, в новом методе InvoicesService мне нужно вставить записи контактов. Как вы думаете, что является правильным местом для размещения этой операции?

  1. Создайте экземпляр и вставьте контакт непосредственно в эту службу (как показано в прикрепленном коде).

Или

  1. Найдите этот код в новом ContactService или внутри домена Contacts и вызовите эту службу из InvoicesService?

В случае записи счета-фактуры, я думаю, эта услуга является правильным местом, потому что она связана с ее контекстом, я прав?

public with sharing class InvoiceService { 

    public static createInvoice(invoiceDTO invoiceDTO){
            
        //Create Contact
        Contact c   = new Contact();
        c.LastName  = invoiceDTO.LastName;
        c.Email     = invoiceDTO.Email;
        insert c;


        //Create Invoice
        Invoice__c invoice = new Invoice();
        invoice.Amount__c = invoiceDTO.Amount;
        invoice.Contact__c = c.Id;
        insert invoice;
    }
}
1
lopez.regalado.fj 29 Июл 2021 в 22:32
2
Просто для ясности, "SoC" здесь относится к разделению интересов.
 – 
Derek F
29 Июл 2021 в 22:03
Спасибо за это. Меня смутило, почему они говорили о системе на чипе.
 – 
sfdcfox
29 Июл 2021 в 22:05

2 ответа

Лучший ответ

Полезным способом приблизиться к этому будет (здесь я использую шаблон fflib)

class ContactsServiceImpl implements IContactsService {

  // example using fflib UnitOfWork
  public void create(Invoice[] invoices) {
     fflib_ISObjectUNitOfWork uow = Application.UnitOfWork.newInstance();
     for (Invoice invoice: invoices) {
          uow.registerNew(new Contact (
             LastName = invoice.LastName,
             ...));
     }
     uow.commitWork();

 }
 // example w/o UnitOfWork
 public void create(Contract[] contracts) {
     Contact[] contacts = new List<Contact>();
     for (Contract contract: contracts) {
          contacts.add(new Contact (
             LastName = contract.CounterPartyName,
             ...));
     }
     insert contacts;
 }

 // example w UnitOfWork and arbitrary wrapper object
 public void create(IContactBuildable contactBuildables) {
     IContactBuildable[] buildables = new List<IContactBuildable>();
     for (Contract contract: contracts) {
          uow.registerNew(contactBuildable.make()));
     }
     insert contacts;
 }
}

interface IContactBuildable {
  Contact make();
}

public class SomeWrapper implements IContactBuildable {
  public SomeWrapper(...someargs...) {..}
  public Contact make() {
    return new Contact(LastName = this.lastName, // some obj variable
                       Email = getEmail() // some obj meth
                       ...);
  }
}

ContactsService.create(new List<SomeWrapper> {  // invoke svc
      new SomeWrapper(..some args),
      new SomeWrapper(..some other args ..)});  

Конечно, у вас может быть общий метод create(SObject[] sobjects), который использует instanceOf для отправки/делегирования различным конструкторам контактов.

Уровень домена контактов не место для размещения логики, поскольку домен отвечает за управление существующими экземплярами контактов (представленными ему триггерами) или другими службами. Помните, что конструктор fflib для домена представляет собой список объектов (то есть уже созданных)

Выполнение логики в службе счетов правдоподобно, особенно если существует множество различных видов сопоставления счета-фактуры с контактом и требуется больше контекста, чем просто сам объект счета-фактуры, чтобы выяснить, как создать контакт.

2
cropredy 2 Авг 2021 в 19:43
Мне нравится этот подход, единственное, что меня беспокоит, это иметь много методов create для разных вариантов использования с разными входными параметрами, sObjects, обернутыми классами….
 – 
lopez.regalado.fj
30 Июл 2021 в 01:21
Ну, вы могли бы иметь create(IContactBuildable[] objs) и заставить метод вызывать метод IContactBuildable make() для каждого объекта, который возвращает Contact - таким образом, вся подробная логика находится в объекте, который представляет вариант использования.
 – 
cropredy
30 Июл 2021 в 01:28
Не могли бы вы немного уточнить свой комментарий, чтобы лучше понять ваше решение? и как это решение согласуется с шаблоном Unit Of Work, предоставленным fflib. Спасибо!
 – 
lopez.regalado.fj
30 Июл 2021 в 03:08
@lopez.regalado.fj - я отредактировал свой ответ
 – 
cropredy
30 Июл 2021 в 07:10

В стандартной реализации учебника InvoiceService вызовет ContactService для создания записи Contact; это гарантирует, что единственный способ создать контакт — из одного места с одной реализацией. На практике это может быть излишним (например, вы можете получить функцию, которая вызывается только один раз), но только вы можете определить, так ли это.

1
sfdcfox 29 Июл 2021 в 22:10