Subject: Re: Struts Architecture From: Vic C <[EMAIL PROTECTED]> === I would call Pet Cemetery "Complexia very BAD Practices for suckers". This was designed to make your application need lots of HW and a new Sun server for each multiple of 50 users. See http://www.softwarereality.com for 101 reasons why this is bad. This is not something that a production or operational system does, this would only work in a lab environment with lots of overhead. Companies that do this find Java expensive to develop and operate. Java is just fine, blame your software engineer/architect who would think of picking this. The best solution is always low tech.
This would be a good practice IMO: 1. Dispatch a "new" parm to action. 2. Action asks frmBean.new(); 3. New in frmBean does DAO.new() to create a row that will hold a new row. (It might go to db to get MetaData). Also this same action does all CRUD, not just new. Same with DAO and frmBean, they do all the CRUD, not just new. (so action has new(); update(); delete(); save(); that get dispatched to it; as does the DAO, etc.) Look at my code. 4. With the empty rowholder with matching meta data in scope, it goes to page. 5. Use inputs some date, and submits a save. (Struts auto populates setters) 6. Action gets a "save" parm. 6.a You call frmBean.validate(); and redirecte if any messages. 7. Action asks frmBeansave(). 8. Bean asks DAO to update. 9. Update enumerates rows; and columns, construction an insert statement. Anyway, I have taken above to production just fine. Simple MVC, not Complexia. Again new people over engineer. KISS is much more powerful and doable in Production. I suggest you always unit test a bean to be able to insert to db, before placing it in Struts. Write a console app to insert into a bean. If it works in console app, it will work in Struts. Oddly, if it does not work in a console unit test; it will not work in Struts, and it is not Struts fault. Sun only makes money on J2EE license, and ... news flash, sales people lie, a lot. My db sample does exactly above. Mike Duffy wrote: > Vic, > > Please take a few moments to review the following, I would value your > opinion before I send it to the Struts user group. > > Thanks in advance for your time and insight. > > Mike > > ##################################################################### > 1. User enters information in an HTML form to create an account. > > 2. Struts Controller creates and populates a new AccountForm with > user input. > > 3. Controller invokes AccountForm.validate() (assume validation > passes). > > 4. Controller invokes AccountAction.perform(). > > 5. From within the method AccountAction.perform(), the form argument > is cast to an AccountForm object. > > 6. From within the method AccountAction.perform(), the method > AccountForm.createAccount() is called to create an Account object. > > The Account object is one of several "ComponentObjects" that > represent the data model of the system and contain getters and > setters for primitive values and other ComponentObjects (in this > example the Account ComponentObject contains an "Address" > ComponentObject and a "User" ComponentObject). The terms > "ValueObject" and "DataTransferObject" have been used in this thread, > but they seem to have conflicting definitions and they seem to be > used for transferring data across a network, not for communicating > between classes. If you examine the "PetStore" reference > implementation you will see that the classes in the "Model" > subpackages are named with common names (Category, Page, Item, > Product). > > As a comment to my initial question that started this thread, the > FormBean never leaves the Action object. The FormBean is decomposed > into one or more ComponentObjects that represent the data model of > the system. > > 7. From within the method AccountForm.getAccount() a utility method > in a mapping class could be called to perform the mapping. > > If you coordinate the property names between your FormBeans and your > ComponentObjects you can use a very handy method, copyProperties(), > in org.apache.commons.beanutils.PropertyUtils. In this case you can > probably do the mapping right in your AccountForm .getAccount() > method. > > 8. From within the method AccountAction.perform() an instance of > AccountDelegate is created. > > No business logic processing takes place in AccountAction; this class > is considered to be in the web layer. AccountDelegate is located in > the business logic layer. The AccountDelegate class does not do any > "heavy" business logic processing. This class simply helps things > along by coordinating the business logic functions at a central > point. > > If you examine the "PetStore" reference implementation you will see > that these types of classes are called "Helper" classes (e.g., > "AccountHelper"). I have also seen the term "Worker" used for these > types of classes. In my opinion, the term "Delegate" seems more > appropriate because the action from AccountAction is delegated to > AccountDelegate. > > 9. From within the method AccountAction.perform() the method > AccountDelegate.createAccount(Account account) is called to create an > account in the data store. > > If "heavy weight" business logic processing was required the method > AccountDelegate.createAccount() might call worker methods in other > classes. For example, if income information was entered in the > original HTML form a method in a UserWorker class could be called to > evaluate status based on income and zip code. This status value > would then be set in the User object and eventually entered into the > data store. > > 10. From within the method AccountDelegate.createAccount() the method > AccountDAOImpl.getInstance() is called. > > AccountDAOImpl is a singleton which reduces the creation of objects. > The strings for the SQL prepared statements are declared as "public > static final". > > AccountDAOImpl implements the interface AccountDAO. This interface > allows for alternate possibilities in retrieving data. > AccountDelegate will be able to use any object that implements > AccountDAO. At some future time there might be an AccountDAOSoapImpl > or a AccountDAOFutureImpl. > > AccountDAOImpl and its helper classes completely encapsulate the data > access layer for CRUDing accounts (create, retrieve, update, and > delete). The methods in AccountDAOImpl take ComponentObjects as > arguments and return ComponentObjects or void. Connections and > transactions are managed in the data access layer not the business > logic layer. The data access layer will have helper classes to > manage connections and transactions (any system that has a > significant number of transactions should probably use an EJB > implementation). > > In essence, when a class in the business logic layer calls a method > in the data access layer, a ComponentObject is sent in and a > ComponentObject is returned; the business logic layer does not affect > any functionality in the data access layer and the data access layer > does not affect any functionality in the business logic layer. > FormBeans and ComponentObjects should not contain data access calls > to "create themselves"; they should be created by methods in the data > access layer. > > From the Java BluePrints website: > > "A clear separation of concern between access objects and components > will enable a component to be adapted to different enterprise > information system resources. For example, a component can use an > access object to adapt its persistent state management to a different > database schema or to a different type of database." > >http://java.sun.com/blueprints/guidelines/designing_enterprise_applications/eis_tier/programming_access/index.html > > 11. From within the method AccountDelegate.createAccount() the method > AccountDAOImpl.createAccount(Account account) is called. > > 12. From within the method AccountDAOImpl.createAccount() a method is > called to get a connection from the connection pool. > > 13. From within the method AccountDAOImpl.createAccount() the method > Connection.setAutoCommit(false) is called (we do not want our > transaction to commit unless every part of the transaction succeeds). > > When updating multiple tables in the same transaction the updates > must be done through the same connection. In this example, we will > assume three tables need to be updated: tblAccount, tblUser, and > tblAddress. > > 14. From within the method AccountDAOImpl.createAccount() the method > account.getUser() is called (the Account object contains a User > object). > > 15. From within the method AccountDAOImpl.createAccount() the method > UserDAOImpl.getInstance() is called. > > 16. From within the method AccountDAOImpl.createAccount() the method > UserDAOImpl.createUser(Connection connection, User user) is called. > > Any exceptions that occur in UserDAOImpl.createUser() are thrown to > the calling method: AccountDAOImpl.createAccount(). If an exception > is caught by AccountDAOImpl.createAccount() the method > connection.rollback() is called. > > The class UserDAOImpl may also have a method createUser(User user) > that independently gets a connection and creates a user. > > 17. From within the method AccountDAOImpl.createAccount() the method > account.getAddress() is called (the Account object contains a Address > object). > > 18. From within the method AccountDAOImpl.createAccount() the method > AddressDAOImpl.getInstance() is called. > > 19. From within the method AccountDAOImpl.createAccount() the method > AddressDAOImpl.createAddress(Connection connection, Address address) > is called. > > Any exceptions that occur in AddressDAOImpl.createAddress() are > thrown to the calling method: AccountDAOImpl.createAccount(). If an > exception is caught by AccountDAOImpl.createAccount() the method > connection.rollback() is called. > > The class AddressDAOImpl may also have a method createAddress(Address > address) that independently gets a connection and creates an address. > > 20. If no exceptions are caught from the methods > UserDAOImpl.createUser() or AddressDAOImpl.createAddress() the method > AccountDAOImpl.createAccount() creates an entry in tblAccount. If no > exception is generate when updating tblAccount the method > connection.commit() is called and the connection is released in the > "Finally" block of AccountDAOImpl.createAccount(). If exceptions > occur, the method connection.rollback() is called. > > When I catch an SQL exception, I usually wrap it as a RunTime > exception and rethrow it. I'll have my error.jsp display an > appropriate message. As a general rule, when the only thing you are > going to do is display a message there is no need to create a > specific exception. > > 21. Assuming nothing has gone wrong, we are now back in our > AccountAction.perform() method and Struts will redirect to the > appropriate forward page. > > When necessary, objects that need to be placed in the session or > request are placed from within the perform() method in the Action > class, not by methods from classes in the business logic layer; the > business logic layer does not affect any functionality in the web > presentation/management layer and the web presentation/management > layer does not affect any functionality in the business logic layer. > > The general idea is to keep the layers of an application separate. > #################################################################### > > __________________________________________________ > Do You Yahoo!? > Yahoo! Health - your guide to health and wellness > http://health.yahoo.com -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

