Domain Transient and Domain Transient Composite

Discussions

J2EE patterns: Domain Transient and Domain Transient Composite

  1. Domain Transient and Domain Transient Composite (5 messages)

    Domain Transient and its composite Domain Transient Composite (DTC) are inter-tier or inter-service patterns designed so that the DTC operates like a coarse-grained Data Transfer Object but also:
     
    A) Organizes business rules to follow their respective use case and domain models.
    B) Allows transfer objects to inherit from the domain and supports structured documents such as XML and SOAP.
    C) Decreases coupling with the transfer mechanism so that it can readily be substituted.

    The example demonstrates these objectives as follows
    In the example below,
    A) Business rules can use bean methods as well as a domain distributed process() method that follows from a use case.
    B) The Composite object can load other Composites to be able to transfer them as an XML/SOAP supporting tree structure.
    C) The transfer() method is isolated and can be replaced without affecting how the objects or Composite are used.

    In addition, the transfer can be single-channel or multi-channel.
    Use case
    ========
    "Skyline OpticNet" is offering a special deal for new customers and existing modem or DSL customers who order fiber broadband by December 1st (and we all should be so lucky). Broadband must be scheduled for installation by January 31st to take advantage of the offer.

    The application needs to collect these preliminary orders. It needs to record the date of the order and the customer who ordered it and send this information to a back end server..

    Architecture:

    A Servlet collects Orders and Customer information. It sends this information to the OrderProcessingService - currently an EJB server. Currently connection from the Servlet to the EJB server the usual RMI/IIOP. But in the near future it may be upgraded to J2EE 1.4 which enables an external SOAP-based Web service call.

    Explanation of example:
    =======================
    There are 5 structures.
    1. A domain class structure headed by Order and Customer

    2. An interface structure for use cases headed by Processable and a single use case called IntakeProcessing

    3. Two DomainTransient classes AdvancedOrder and InitialCustomerIntake that inherit from the domain and implement the use case.

    - AdvancedOrder inherits from Order and implements IntakeProcessing
    - InitialCustomerIntake inherits from Customer and implements IntakeProcessing.

    4. A Domain Transient Composite (DTC) class: IntakeTransferGroup that holds Processable objects

    5. A servlet that initiate the transfer of IntakeTransferGroup to the back-end service.

    Source code for the example
    ===========================

    1. The domain structure is headed by simple domain classes Order:
    ===============================================
    public class Order {
      // Minimal order has orderID and customerID;
      public String orderId;
      public String customerId;
      
      public String getOrderId() {
         return orderId;
      }
      public String getCustomerId() {
         return customerId;
      }
      public void setOrderId(String neworderId) {
         this.orderId=neworderId;
      }
      public void setCustomerId(String customerId) {
         this.customerId=customerId;
      }
      
        
    }
    and Customer.
    ===============================================
    public class Customer {
      String customerId;

      public String getCustomerId() {
        return customerId;
      }

      public void setCustomerId(String newCustomerId) {
        customerId = newCustomerId;
      }
    }

    2. The Processing structure is a set of interfaces headed by "Processable." and extended with IntakeProcessing
    ===============================================
         public interface Processable {
            public void process();
         }

    - IntakeProcessing has one method process() that is implemented by DomainTransients that are part of the Intake processing.
    ===============================================
         public interface IntakeProcess extends Processable {
         }

    3. Out of the domain and processing structures there are two DomainTransients
    - AdvancedOrder
    ===============================================
    import java.util.Date;
    import java.util.GregorianCalendar;
    public class AdvancedOrder extends Order implements IntakeProcess {

      public Date orderDate;
      
     // Business rules for the IntakeProcess:
      public void process() {
         // Set the date.
         GregorianCalendar gc = new GregorianCalendar();
         orderDate=gc.getTime();
       
      }

       /** Include transfer() for individual DomainTransient (but not necessary for Composite)
       */
       public AdvancedOrder transfer() {
        // Include serialization or other transfer code here.
         AdvancedOrder v= this; // "Null transfer" for testing.
         return v;
      }
    }

    and an InitialCustomerEntry
    ===============================================

    public class InitialCustomerEntry extends Customer implements IntakeProcess {
      public String telephone;
      String emailaddress;
      String name;

      public void process() {
      // Add business rules here.
        
      }

      public String getEmailaddress() {
        return emailaddress;
      }

      public void setEmailaddress(String newEmailaddress) {
        emailaddress = newEmailaddress;
      }

      public String getName() {
        return name;
      }

      public void setName(String newName) {
        name = newName;
      }
      
    }

    4. There is a Composite class called IntakeTransferGroup
    ===============================================
    public class IntakeTransferGroup implements IntakeProcess
    {
      public final static int _ORDER=0;
      public final static int _CUSTOMER=1;
      public static final int NITEMS=2;
      
      Processable [] items = new Processable[2];
      
      public IntakeTransferGroup() {
        super();
      }
      int itemtoload=0;
      /** Set size of TransferGroup
       */
      public void IntakeTransferGroup(int nitems) {
       items= new Processable[nitems];
      }
      /** Initialize items that have been loaded into the group
      */
      public void process() {
        for (int i=0; i<NITEMS; i++) {
         items[i].process();
        }
      }

      /** Load transferable item into TransferGroup (in sequence given)
      */
      public void loadObject(Processable obj) {
          items[itemtoload++]=obj;
      }
      public void loadObject(int itemin, Processable obj) {
          items[itemin]=obj;
      }

      /** Retrieve transferred item from TransferGroup
      */
      public Object getObject(int i) {
         return items[i];
      }
      
      public IntakeTransferGroup transfer () {
      
      // Include serializing, and/or ejb call code, or or other transfer code here.
      // E.g.

     /*
        Context jndiContext = new InitialContext();
        Object ref = jndiContext.lookup("OrderSessionHome");
        OrderSessionHome orderSessionHome
          = (OrderSessionHome) PortableRemoteObject.narrow(ref, OrderSessionHome.class);
        OrderSessionRemote order = orderSessionHome.create();
    */

         
         return (IntakeTransferGroup) nullModemSendExample();

     
      }
      public IntakeTransferGroup nullModemSendExample() {
          return (IntakeTransferGroup) this;
      }

    }
    5. The Main Servlet
    ========================================
    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.io.*;
    import java.util.*;

    public class TestTransferGroup extends HttpServlet {
      private static final String CONTENT_TYPE = "text/html; charset=windows-1252";
      public void init(ServletConfig config) throws ServletException {
         super.init(config);
      }
      public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
      }

      public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name="";
        String address="";
        String orderid="can not complete";
        String customerid="not found and cannot create.";
        
        try {
          // 1. The servlet retrieves parameters from the user for the Customer Name and e-mail address.
          name = request.getParameter("custname");
          address = request.getParameter("custaddr");

         // 2. The servlet creates the DomainTransients and sets their values.
          AdvancedOrder ao= new AdvancedOrder();
          InitialCustomerEntry ic = new InitialCustomerEntry();
          ic.setEmailaddress(address);
          ic.setName(name);

         // 3. The servlet loads the transients into the Composite.
          IntakeTransferGroup tg= new IntakeTransferGroup();
          tg.loadObject(IntakeTransferGroup._CUSTOMER, ic);
          tg.loadObject(IntakeTransferGroup._ORDER,ao);

         // 4. The servlet processes the Composite - which in turn causes processing of each Domain Transient.
         // When AdvancedOrder.process() is called, it sets the time that the order was placed.
          tg.process();

         // 5. The servlet tells the Composite to transfer itself to the EJB.
          IntakeTransferGroup tg2=tg.transfer();

         // 6. The servlet retrieves the newly assigned Order ID and Customer ID
          AdvancedOrder ao2 = (AdvancedOrder) tg2.getObject(IntakeTransferGroup._ORDER);
          InitialCustomerEntry ic2 = (InitialCustomerEntry) tg2.getObject(IntakeTransferGroup._CUSTOMER);
          orderid=ao2.getOrderId();
          customerid=ic2.getCustomerId();
          
        } catch(Exception e) {
          e.printStackTrace();
        }

        response.setContentType(CONTENT_TYPE);
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>TestTransferGroup</title></head>");
        out.println("<body>");
        out.println("<p>Order placed for Customer ID:"+customerid);
        out.println("<BR>Your order number is:"+orderid );
        out.println("</p></body></html>");
        out.close();
      }
    }

  2. Summary
    =======
    - For a summary of processing: See the six steps listed in the Servlet doPost() method body.

    - The process() method is invoked on the Composite causes activation of process()in all the contained objects - including another Composite if you choose to load one that implements the "Processable" interface.


    Issues and Questions:
    =====================
    Multiple Use cases/Multiple roles: The current example has one Use case step called "IntakeProcessing" In a larger application that has multiple use cases, is it better to have one set of objects that inherits multiple use case steps? If so, should these be organized by role?
    Or should there be only one set per use case?

    Supporting front-end design: From the servlet's point of view, the object transfered is a bean - or bean Composite - so I guess this supports a front-end design that is bean based - which hopefully is a good thing.

    Developmental: This is essentially a "Composite bean" mechanism that also serves as a transport. There are individual rules that may need to be implemented for each field. The bean nature of the pattern supports this - I think.

    However, if design work on the use case has not been done, a tension can be created that the pattern doesn't help. There can be a tension between "trying to get inter-tier transfer to work" and the amount of time writing a composite bean might take in the absense of a good story board and/or good use case analysis. Of course, if you've seen the pattern, you now know it isn't all that complicated so maybe it's Ok.

    Business rule organization: Business rules can be implemented procedurally (as in the servlet)or they can be distributed (as with the process() method). Which is better? For supporting MVC?

     
  3. Now THAT's what I'm talking about.

    OK, this is all pretty clear as to what's going on. It looks like you are proposing some sort of souped-up BusinessDelegate pattern where the EJB calls are abstracted away in the various transfer or process methods.

    The question is, why? What does this buy you? Why is this better than just using BusinessDelegates and customDTOs? You could have just as easily created a custom DTO instead of your IntakeTransferGroup, or even used a Hashtable as your DTO. That DTO is your parameter to your EJB call, and that EJB call returns either an instance of that class, or maybe another kind of class encapsulating the results.

    What do we get by abstracting all this into a Processable interface and all that, especially since some use cases may not fit the model of being "processed" (e.g. a search, or storing data directly)
  4. Hi Dave C,

    Thanks for asking reasonable questions that promote discourse.

    "some souped-up BusinessDelegate pattern where the EJB calls are abstracted away in the various transfer or process methods. "

    I think it's different from Business Delegate - not better or worse. Business delegate does a good job of taming the EJB interface. I didn't do that. I could have I guess (by using Business Delegate).

    "What does this buy you?"

    The goals were clearly stated and how they are achieved is stated as well. I will restate them briefly:

    1. Business processing rules explicit and organized.
    2. Support for tree-structured interfaces
    3. EJB or other interaface is easily replaced.

    This is not quite the same as Business Delegate.

    "Why is this better than just using BusinessDelegates and customDTOs?"

    Business Delegate could be used in conjuntion - so again it's not better or worse.

    For CustomDTO, please point me to an example so I can answer on that. Also what are the goals of a "CustomDTO" is as opposed to any of the DTOs defined by Floyd?

    In essense a DTC *is* a DTO that is based on Craig Larman's "Conceptual Class" (Larman98), Section 10.2, page 133 in the original version of the book. Larman claims the additional goals of being "interesting and meaningful."

    "You could have just as easily created a custom DTO instead of your IntakeTransferGroup"

    Again, show me some code. On what basis would you assign a name for the customDTO?

    "or even used a Hashtable as your DTO." - Been there. Done that. Has its good and bad points. See also Floyds, Data Transfer Hashmap.

    "What do we get by abstracting all this into a Processable interface"

    - Classes that can be used in a Composite pattern.
    - Clarity on which use cases are implemented.
    - Clarity on which Java interfaces represent a use case.

    I guess you could call the top level interface
      "UseCaseStepInterface" instead of
      "Processable"
    if you want. I was just picking something like what I'm accustommed to seeing as a top level interface in Java (something ending in "ble").

    "some use cases may not fit the model of being "processed" (e.g. a search, or storing data directly)"

    I once made that same argument a long time ago (I think I lost). But what's your suggestion here (or was it "UseCaseStep"?

    Regards,


    Rich
  5. I guess what I'm saying is that what you are proposing seems overly complex with little benefit. More specifically, I mean that a simpler approach would implement the use case you proposed, and simple approaches are often desirable.

    In your example of collecting an order and customer information, the most straightforward approach that comes to mind is to make a session EJB:

    public interface OrderProcessor
    {
      public OrderProcessingResult processOrder(OrderRequest o);
    }

    // Not sure what this needs, because your use case doesn't
    // say what the UI is after sending an order. I'll assume
    // it's just a "thanks, and here's your tracking number"
    public class OrderProcessingResult
    {
      public String getTrackingNumber();
      // true if the user provided bad info and needs to resubmit
      public boolean wasUserError();
      // if wasUserError true, returns a human-readable message
      // explaining what they need to fix.
      public String getUserErrorDescription();
    }

    OrderRequest holds all data the user must provide in the UI, including information about themselves, and information about the order (presumably the business rules of placing an order define the required data to do so; this class then describes all such data that is expected to be provided from the user).

    Then, your servlet might look like this:

    public void service(...)
    {
       OrderRequest orderRequest = new OrderRequest();
       orderRequest.setName(httpRequest.getAttribute("name"));
       // or whatever, you get data from the request and shove
       // it into orderRequest

       OrderProcessingResult result = orderProcessingBusinessDelegate.processOrder(orderRequest);

       if (!result.wasUserError())
       {
          // send them to the thank-you page w/ their tracking number
       }
       else
       {
          // send them back to the data collection page with the error message
       }
    }

    Now, that is pretty simple, and your EJB/BusinessDelegate method won't have to change if you add/remove/change that type of data your order processing business process needs.

    Your stated goals of YOUR proposal are:

    1. Business processing rules explicit and organized.
     - Well, my simple implementation is organized and the business _process_ is explict in the client (made possibly by the EJB session bean interface), and the _rule_ is abstracted into the EJB and not on the client.

    2. Support for tree-structured interfaces
     - I don't know what this means, or why it's an advantage, or how NOT using your proposed architecture PREVENTs this.

    3. EJB or other interaface is easily replaced.
     - I would wager that very few systems need to be designed to completely replace the back-end infrastructure, and so I don't see this as an advantage, because I don't see that this would be a common requirement. If it were, however, the business delegates techincally abstract away the session bean use and could be rewritten to use something else w/out the client code having to change at all.

    Now, comparing what I've written above to what you have done, your proposal is more complex, with more layers of abstraction, and I guess I just don't get what the advantage is. My code was easy to write, it seems very clear as to what's going on, and it doesn't do anything funky (simply because nothing funky is warranted).

    So, what's wrong with that?
  6. Hi Dave,

    There is nothing wrong with your implementation. OrderRequest sounds like a good concept for a class and Business Delegate is a fine pattern to use with it.

    It appears to me that where you are saying things are "overly complex" and the benefits you are questioning are all ones that are related to Web services:
     
    - Using Tree structures (the Composite pattern)
    - Decoupling so the front-end/back-end interface can be upgraded.

    The book I'm writing is about J2EE Web services. Tree structures are important because that's how XML and SOAP documents are written and that's how services and tiers need to communicate to use Web services.

    But it's not all just about WSDL/SOAP. There has to be some kind of on-ramp for Web services. That's where decoupling comes in.

    The most immediate decoupling comes from the fact that J2EE V1.4 will support Web service communcations to EJB containers.

    I'm sorry if these benefits have not made sense thus far. Maybe knowing this is about J2EE Web services will explain things.

    Thank you for your comments and insights.

    Regards,


    Rich