Page 3 of 4 FirstFirst 1234 LastLast
Results 21 to 30 of 39

Thread: Generic User Management conflicts with Rich Domain Model

  1. #21
    Join Date
    Aug 2004
    Location
    Germany, Magdeburg
    Posts
    279

    Default

    Hi thyrell,

    Wow this is a long reply, thanks for your afford!

    Hmm, your document repository is that a part of your domain (on p172 fig 7.4, your document repository would be direct interchangable with, say "Cargo" ... of course it would not in the shipping business, but you know what i mean). Is that the case? Then your document repository is part of the domain model, but it is not the same thing like a CargoRepository - which your case would be a DocumentRepositoryRepository. Huh, i hope it is understandable, what i want to say.
    You are understandable, don't worry. But why do you think this repository is part of the domain? I usally follow a step by step process:

    1. Identify the requirements and the responsibilities for the business objects (including domain objects).

    2. Introduce another collection of objects needed by the business objects to do there tasks. This is mostly where the domain model takes shapes for me, thats why I think repositories are domain model objects unless they are not business objects (not required to talk about the business domain).

    3. Introduce other objects needed to implement the domain model related stuff, these are implementation details like DAO stuff or String objects... .

    It is quite obvious that there is a difference between the User of the business domain model and the User of the actual implemented domain model. This is why I always express this by using an interface for the business domain user and a concrete implementation for the domain model User.

    And the DocumentRepository is just something being introduced to model the fact that the documents are stored. I guess it is the onion model that tricks us here. I like to model the whole lifecycle of any object that appears to be dynamic during the lifecycle of the system (if a system uses persistence data, the system just lives on, if the hardware is shut down).


    Yes, depot might fit too. I agree with your definition of the collection. It appears as such a collection. And the implementation of the repository maps it to whatever is required (database, xml, memory).
    I don't know but I like depot even more. You know for me a depot is actively managed where as a repository appears to be passive itself (just like a list). But maybe this is just a picture in my mind ;-).


    Yep. But he does not use those repositories within aggregates. They are used by the client to coordinate things (see P158 Figure 6.23). Now what is a client? On p156 he states (second bullet): "leave transaction control to the client". this makes it clear what a client in my project is: a service (my UserService). I think a client is whoever uses the domain model, which could basically everything except the entities/values. that's how i'd define it.
    That is the problem by using the term client, I guess. Every unit of the system is designed to exist for a given purpose. And the only purpose a system might have is to be senseful (in a designed system, not in a real world). And to be senseful, you have to perform a task for someone (other system, other unit or what else). You know client used here is just meaning everything. The question is where does the client ends? How about the client of the client?

    But following the onion model it is quite clear. The domain layer (Evans term) serves the application layer, which serves the user interface layer which serves the outside. So the reason of existence of the system is the existence of the outside.

    This is quite obvious when you remember that all functional requirements come from the outside and travel towards the next layer (deeper). So the reason for existence just travels in the opposite direction than the requirements go (requirements define which solution is senseful). And if two things travel in opposite directions, it is quite likely that both are related somehow. (like of every money transaction, where the decrease of the money counter of one account directly opposites the increase of money of the other one).

    so if your address would have been built by a factory would you say the factory is part of a the business domain model? no, because the factory is a implementation detail.
    The problem with this factory thinking is that a factory is nothing more then a valid starting point of the lifecycle of an object. The body of my mother factored me. But I am not likely to model such biological reality when talking about user registries. So a hypothetical factory just replaces the biological factory and so users can be created out of thin air. So a factory mostly substitutes a world detail that is not modeled (just a detail left out).

    So a value object called address is an expression of a real address which was created by the postal dudes. ;-)


    You could have both (Registry+ Factory) and combine them in a service which also may apply a transactional boundary or something else.
    Which would be a manager. A manager is something that can manage a lifecycle of objects. That's why I would like to express this aspect within the domain model.


    I agree, DAO does implies persistence somehow. whereas REPOSITORY not necessarily does.
    The problem is that both are implementation details. Mostly a repository is used to support a very limited count of business objects (or even domain model elements). So I am not sure if this is interchangeable.


    A when using a repository you really don't care and do not know anything about if the objects "in the repository" are coming from a database, a xml file, held in memory, are loaded by each method call or not, are cached or not, are retrieved and reconstructed from 3 different sources... If a need a specific object i just ask a repository to give it to me. That's so cool imho.
    Well just use interfaces to define all your public objects (visible to the outside) and all you will say is cool ;-). That's why we all try to program against as abstract objects as possible, and there is nothing more abstracter than a well defined interface. ;-)


    I have my Repsository that takes care of it.
    But check if there is an even more comprehensive object possible. For example, why should I create a special repository 'layer' if an implementation of the user manager can take care of it. Everything that work is related to becomes just an implementation detail. For the outside the client of the repositories are responsible. Don't add repositories just to have repositories - I don't think you are doing that, but this is what happens to most users of the DAO, there has to be a DAO object... .

    Hmm. I'd say a DAO is a facade that connects the "persistence layer" (hibernate, jdbc, ibatis) to anything else. But do i really want that? I don't.
    Someone has to abstract and play the role of a facade. Just check your business objects. They are facades to hide all the implementation stuff the business objects use themselves to delegate the work. On package level the parent package is a facade of its child packages, if not you screwed you packaging up ;-).

    I think his Application layer is basically a facade to the domain service layer (see p107). It's just a more fine grained seperation that might make sense when serveral domain services need to be combined in one application service call.
    I would say the Application layer extends the domain layer with additional logic needed by the UI interfaces... . So it is like an extension of the domain layer (but a application specific one).


    I see you point. But in which case you would need the ConcreteDAO? What are the pros of using another layer vs Hibernate (or a query QueryObject) to retrieve/store the domain object? To me this is another abstraction ob the infrastructure layer that does not make any sense.
    If you don't use repositories, it is the task of the DAO to take care for the services the repositories provide... .


    If i want to test the UserRegistry it's simply a mock to an in memory storage (simple collection or so).
    Depends on the implementation of the registry. You know someone has to be adding Hibernate or what every to the picture. So if you don't have a DAO you might end up with a repository to speak the hibernate language internally. That's when you have to test it. Another thing is the mock issue. There is at least an acceptance test to take care for the integration of the things you mock in one test-suite. The question is, if it is suitable for an acceptance test to do this work. I am currently against it to mock-test the domain objects directly delegating there work towards the persistence layer.


    Yeah, this is really a great book. I found it especially useful in combination with Fowlers Pattern reference in Patterns of Enterprise Application Architecture. Those two books should belong togther all the time.
    Yeah they both fit my book shelf very well (beside others...). What kind of books did you read lately? Anything I should also read?


    They would much better display as i.e. *PersistenceSupport and PersistenceException or something that is less limiting. Maybe something the spring people want to think about (though it would break with Rods book).
    Yeah I also have the same thinking. But I guess this isn't that urgent. Maybe you can raise and issue and ask for it to be a matter of consideration when Spring 2.0 is planned.


    Cheers,

    Martin (Kersten)[/quote]

  2. #22

    Default

    You are understandable, don't worry. But why do you think this repository is part of the domain? I usally follow a step by step process:
    Oh, i understood it that way. I guess I misunderstood it.

    And the DocumentRepository is just something being introduced to model the fact that the documents are stored. I guess it is the onion model that tricks us here. I like to model the whole lifecycle of any object that appears to be dynamic during the lifecycle of the system (if a system uses persistence data, the system just lives on, if the hardware is shut down).

    Yep, and the repository would take care of it if the requirements determine such a persistence behavior.

    I don't know but I like depot even more. You know for me a depot is actively managed where as a repository appears to be passive itself (just like a list). But maybe this is just a picture in my mind .
    Hmm, yeah maybe. CVS repository is that kind of thing i guess. Whereas a perforce repository is actively managed imho. But then again, different language different thing. To me the word "Repository" is quite neutral. I drop and pull things there and don't care much what's happening and trust on the repository that everyhting is fine. Though, english is not my native language and Repository is possibly valued different by natives.

    You know client used here is just meaning everything. The question is where does the client ends? How about the client of the client?
    Hehe now it becomes philosophical.

    But following the onion model it is quite clear. The domain layer (Evans term) serves the application layer, which serves the user interface layer which serves the outside. So the reason of existence of the system is the existence of the outside.
    I think that's quite reasonable thinking. I think the problem here is to draw a clean line between the UI Layer and the "application" that solves problems.

    But check if there is an even more comprehensive object possible. For example, why should I create a special repository 'layer' if an implementation of the user manager can take care of it.
    It's not an additional layer i create. I replaced the former DAO Layer with the Repository which now is part of the implementation of my model and not a mere connector to my persistence infrastructure. I'll post some sketeches wenn i'm through finished the project. So then it becomes more clear i guess.

    Someone has to abstract and play the role of a facade. Just check your business objects. They are facades to hide all the implementation stuff the business objects use themselves to delegate the work. On package level the parent package is a facade of its child packages, if not you screwed you packaging up .
    Heheheh. Yeah, the Repository is a facade to the persistence infrastructure. So I'm fine. But i don't see a need putting another facade in between:

    Code:
    UserRepository {
       User findUserById(id) {
            return userDao.gerUserById(id);
       }
    }
    instead i prefer:

    Code:
    UserRepository {
      IUser findUserById(Id) {
            return (IUser) getHibernateTemplate().load(User.class, id); 
      }
    }
    I would say the Application layer extends the domain layer with additional logic needed by the UI interfaces... . So it is like an extension of the domain layer (but a application specific one).
    Agreed.

    If you don't use repositories, it is the task of the DAO to take care for the services the repositories provide... .
    Ok, and then on the other hand if i use Repositories i don't have a need for DAOs.

    Yeah they both fit my book shelf very well (beside others...). What kind of books did you read lately? Anything I should also read?
    Hmm, i read the Spring book (J2EE development witout EJB) which actually brought me to spring. Spring in Action (pretty basic, more for spring beginners, i did not really read it through though) and "Hibernate in Action", also very good. As well as "The Design of Sites" which is more a web-designer's patterns book, more of a reference to look things up. But very handy if you design UIs.

    Yeah I also have the same thinking. But I guess this isn't that urgent. Maybe you can raise and issue and ask for it to be a matter of consideration when Spring 2.0 is planned.
    Yeah absolutely, it's nothing urgent.

    Now, back to the code... before i loose interest That's a big problem, I like it much more to lab things out than to do the actual coding work

    -andi

  3. #23
    Join Date
    Aug 2004
    Location
    Germany, Magdeburg
    Posts
    279

    Default

    It's not an additional layer i create. I replaced the former DAO Layer with the Repository which now is part of the implementation of my model and not a mere connector to my persistence infrastructure. I'll post some sketeches wenn i'm through finished the project. So then it becomes more clear i guess.
    I am looking forward. But I don't know, I just think a repository is just very limited as an object (single responsibility). I like seeing objects comming to life and to demand additional responsibilities. And I don't know but repositories doesn't look being that smart. Anyways, lets see how your code envolves. Hopefully you will keep me up to date! ;-)


    Ok, and then on the other hand if i use Repositories i don't have a need for DAOs.
    Sure! I also agree with that.

    Hmm, i read the Spring book (J2EE development witout EJB) which actually brought me to spring. Spring in Action (pretty basic, more for spring beginners, i did not really read it through though) and "Hibernate in Action", also very good. As well as "The Design of Sites" which is more a web-designer's patterns book, more of a reference to look things up. But very handy if you design UIs.
    Hibernate and 'J2EE without EJB' I already own. I also have a copy of 'Pro Spring', which is quite good and I learned something new from it. Lately I got my hands on 'Agile Documentation' (which isn't that useful for me, but a good read anyways), McConnell's 'Software project survival guide' and 'Code Complete 2nd Edition' are great books and 'Death March' is quite entertaining and also worth reading. There were some more books but they don't made it on my recommendation list. (like Sun's JMX and Java performance tuning book and those books of the 'software metrics series')

    'The Design of Sites' book looks like a good book but Amazon (de) does not provide it yet. I will put it on my wishlist and hopefully I can get a copy of this book. Thanks for the tip.


    Cheers,

    Martin (Kersten)

  4. #24

    Default

    'The Design of Sites' book looks like a good book but Amazon (de) does not provide it yet. I will put it on my wishlist and hopefully I can get a copy of this book. Thanks for the tip.
    http://www.amazon.de/exec/obidos/ASIN/020172149X/

    That's the one. It does not show up if you search by it. Very strange.

    -andi

  5. #25
    Join Date
    Aug 2004
    Location
    Germany, Magdeburg
    Posts
    279

    Default

    Well thanks andi, I brought a copy and I am looking forward to read it.


    Cheers,

    Martin (Kersten)

  6. #26
    Join Date
    Aug 2004
    Location
    Germany, Magdeburg
    Posts
    279

    Default

    Hi Andi,

    got a grip on that book. It's quite well and very interesting but the patterns are quite coarse-grained. To much stuff for a single pattern, if you ask me. Anyways quite a great information source. Thanks for the tip.

    Another issue: I am doing some work related to Public Key Infrastructres and I learned a new word there: Depositories. Never ran across it before. It seams to be a combination of depot and repositories and refers to a banking save or just a secure place you store things at. Might this a nice word to describe a secure or manager like repository. What do you think?


    Cheers,

    Martin (Kersten)

    PS: Boy I scrolled this second page all the way down. I guess this is one of the longest pages this forum has seen so far ;-).

  7. #27

    Default

    Hi Martin,

    it's more for a designer than a developer and different style. I come from both worlds so i'm used to it anyway. matter of taste i guess..

    depository sounds interesting. i'm going with repository though and i refactored them a bit now they look like:

    Code:
    UserRepository {
       private RepositoryStrategy strategy;
    
       setStrategy(...);  // i.e. HibernateRepositoryStrategy
    
       User findUserByName(String name) {
            Criteria specs = strategy.getCriteria();
            specs.add(Restrictions.eq("name", name));
            return (User) strategy.soleMatch(specs);
      }
    }
    The specs are currently the hibernate Criteria api, which is somewhat unclean to use them in the Repository directly. But that works perfectly for now and refactoring towards my own Specification API will be straigt forward. All my persistence code is in one pluggable class "HibernateRepositoryStrategy" and nicely hidden. repositories are clean and working fine. Now I'm happy

    I also ditched coding my model classes (like User, TimeInterval, Name) to interfaces (i mean "interface" contracts). Refactoring became a mess. Maybe I need to add them later, but currently there's no need and just complicates things.

    Yeah, the thread is quite long. I hope it is useful for others than us too...

    -andi

  8. #28
    Join Date
    Aug 2004
    Location
    Germany, Magdeburg
    Posts
    279

    Default

    it's more for a designer than a developer and different style. I come from both worlds so i'm used to it anyway. matter of taste i guess..
    I also like to read books from the other side of the edge. But I don't like the way they used to write down those patterns. Some of the pattern fun is missing and I had to add it myself. If you abstract all those patterns you get a concept collection that helps you in nearly every area. That's what I missed most (beside the forces section), but anyway a great knowledge source.


    All my persistence code is in one pluggable class "HibernateRepositoryStrategy" and nicely hidden. repositories are clean and working fine. Now I'm happy Smile
    That's nice to hear. Do you use the same class with diffrent persistance strategies or do you use this strategy 'just for' organisation of your logic?

    In my current situations, I didn't like the strategy pattern that much. I simply had an iBatis implementation and that is all? I will fall back for those strategy pattern once, I need it.

    Also I miss the addUser method in your repository :-). How is the repository finally be used? Where is the user added and removed? Where are the update methods? Can you please outline the dependencies of this repository? You know, who uses it and for what... .


    Cheers,

    Martin (Kersten)

    PS: I brought 'JavaServer Faces in Action' lately - just to keep me in touch with JSF. Do you know of a good book about JDO? This is next on my 'to investigate' list.

  9. #29

    Default

    Hi Martin,

    Code:
    That's nice to hear. Do you use the same class with diffrent persistance strategies or do you use this strategy 'just for' organisation of your logic?
    Currently i use it with hibernate only. But the idea is to make a switch as smooth as possible. So, basically it's currently only 'just' for organization.

    Code:
    Also I miss the addUser method in your repository . How is the repository finally be used? Where is the user added and removed? Where are the update methods? Can you please outline the dependencies of this repository? You know, who uses it and for what...
    I did not refactor all the repositories to the strategy pattern. The example is not the whole code just typed it on the fly. Also i still do not have diagrams in digital form (i'm lazy...).

    The following repository is working though (used to store/lookup hashes for the user enrollment, the name will change eventually):

    Code:
    public class RegistrationTicketRepository {
    
    	/**
    	 * The persistence strategy used for this repository
    	 */
    	private RepositoryStrategy strategy;
    
    	/**
    	 * Private construcor. This class should never be instantiatet manually.
    	 */
    	private RegistrationTicketRepository() {}
    
    	// ~ Bean setters/getters --------------------------------------------------
    
    	public void setStrategy(RepositoryStrategy strategy) {
    		this.strategy = strategy;
    	}
    
    	// ~ Methods ---------------------------------------------------------------
    
    	public RegistrationTicket findTicket(String hash)  {
    		// This is OK but not very clean since the hibernate
    		// criteria api bleeds into the repository that should not be
    		// A domain criteria specification object wwhich the strategy is
    		// aware of should be passed to the strategy here
    		// nevertheless this code is easie to refactor if the persistence
    		// layer would change.
    		Criteria crit = strategy.createCriteria(RegistrationTicket.class);
    		crit.add(Restrictions.eq("hash", hash));
    		return (RegistrationTicket) strategy.soleMatch(crit);
    	}
    
    	public void storeTicket(RegistrationTicket ticket) {
    		strategy.save(ticket);
    	}
    
    	public void removeTicket(RegistrationTicket ticket) {
    		if (!ticket.isValid()) {
    			strategy.remove(ticket);
    		} else {
    			// TODO-HIGH: convert to business exception
    			throw new RuntimeException("Can't remove a valid ticket. Please invalidat first.");
    		}
    	}
    
    }
    The ticket classes
    Code:
    /**
     * An abstract Ticket. A <em>Ticket</em> is basically a object that holds a
     * world wide unique and random hash &#40;GUID&#41;. A Ticket should be subclassed to
     * provided case specific behavior and data.
     *
     * @author  Andreas Aderhold
     * @version $Revision$
     */
    public abstract class Ticket extends Entity implements Serializable &#123;
    
    	private String  hash = "";
    	private boolean valid = false;
    	private Date created = new Date&#40;&#41;;
    
    	Ticket&#40;&#41; &#123;&#125;
    
    	public String getHash&#40;&#41; &#123;
    		return hash;
    	&#125;
    
    	protected String buildNewHash&#40;&#41; &#123;
    		this.hash = AnotherGuid.getInstance&#40;&#41;.genNewGuid&#40;&#41;;
    		this.valid = true;
    		return getHash&#40;&#41;;
    	&#125;
    
    	public void invalidate&#40;&#41; &#123;
    		this.valid = false;
    	&#125;
    
    	public boolean isValid&#40;&#41; &#123;
    		return this.valid;
    	&#125;
    
    	public Date created&#40;&#41; &#123;
    		return this.created;
    	&#125;
    
    &#125;
    
    public class RegistrationTicket extends Ticket &#123;
    
    	public static final RegistrationTicket EMPTY_TICKET = new RegistrationTicket&#40;&#41;;
    
    	private User user;
    
    	RegistrationTicket&#40;&#41; &#123; super&#40;&#41;;	&#125;
    
    	private RegistrationTicket&#40;User user&#41; &#123;
    		this.user = user;
    		buildNewHash&#40;&#41;;
    	&#125;
    
    	public User getUser&#40;&#41; &#123;
    		return user;
    	&#125;
    
    	public void validateUser&#40;&#41; &#123;
    		if &#40;isValid&#40;&#41;&#41; &#123;
    			if &#40;!user.isEnabled&#40;&#41;&#41; &#123;
    				user.setEnabled&#40;true&#41;;
    			&#125;
    			invalidate&#40;&#41;;
    		&#125;
    	&#125;
    
    	public static RegistrationTicket newTicketForUser&#40;User user&#41; &#123;
    		if &#40;user == null&#41; &#123;
    			throw new IllegalArgumentException&#40;"The user can't be null."&#41;;
    		&#125;
    		return new RegistrationTicket&#40;user&#41;;
    	&#125;
    
    	public boolean equals&#40;Object o&#41; &#123;
    	    if &#40;this == o&#41; return true;
    	    if &#40;!&#40;o instanceof RegistrationTicket&#41;&#41; return false;
    	    final RegistrationTicket ticket = &#40;RegistrationTicket&#41; o;
    	    if &#40;!getHash&#40;&#41;.equals&#40;ticket.getHash&#40;&#41;&#41;&#41; return false;
    		if &#40;!getId&#40;&#41;.equals&#40;ticket.getId&#40;&#41;&#41;&#41; return false;
    	    return true;
    	&#125;
    
    	public int hashCode&#40;&#41; &#123;
    		return this.getHash&#40;&#41;.hashCode&#40;&#41;;
    	&#125;
    
    	public String toString&#40;&#41; &#123;
    		return "RegistrationTicket &#40;HASH= "+ getHash&#40;&#41; +"&#41;";
    	&#125;
    
    &#125;

    The repository is used by a service:

    Code:
    public interface IRegistrationService &#123;
    
    	/**
    	 * Register a new User with the system.
    	 * <p>
    	 * This method should throw an &#123;@link UserExistsException&#125; if the user is
    	 * already in the database and a &#123;@link EmailAddressExistsException&#125; if
    	 * the Email addess of the user is already taken by another user.
    	 * <p>
    	 * This methdod is also responsible for sending a email to the user that
    	 * the enrollment process started.
    	 * <p>
    	 * This can be done by a advisor wich sends a email to the user. It can
    	 * also be done programatically by sending the mail directly within the
    	 * implementation. Maybe the preffered choice.
    	 * <p>
    	 *
    	 * @param  user The configured User to sign up.
    	 * @throws BusinessException in case of errors
    	 * @return <code>true</code> if the registration completed;
    	 *         <code>false</code> otherwise
    	 */
    	abstract boolean registerUser&#40;User user&#41; throws BusinessException;
    
    	/**
    	 * Enroll a registered user.
    	 * <p>
    	 * Completes the final enrollment of a user by checking against a hash.
    	 * If the hash can be found in the pending enrollemnts the corresponding
    	 * User is set active and the pending enrollment is deleted.
    	 * <p>
    	 * This methdod is also responsible for sending a email to the user that
    	 * the enrollment process successfully completed.
    	 * <p>
    	 * This can be done by a advisor wich sends a email to the user. It can
    	 * also be done programatically by sending the mail directly within the
    	 * implementation. Maybe the preffered choice.
    	 *
    	 * @param  hash
    	 * @throws BusinessException
    	 * @return The enrolled <code>User</code>
    	 *
    	 */
    	abstract User enroll&#40;String hash&#41; throws BusinessException;
    &#125;
    Implementations
    Code:
    	public boolean registerUser&#40;User user&#41; &#123;
    
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Starting signup for user " + user.getUsername&#40;&#41;&#41;;
    		&#125;
    
    		if &#40;userRepository.findUserByUsername&#40;user.getUsername&#40;&#41;&#41; != null&#41; &#123;
    			throw new UserExistsException&#40;&#41;;
    		&#125;
    		if &#40;userRepository.findUserByEmail&#40;user.getEmail&#40;&#41;&#41; != null&#41; &#123;
    			throw new EmailAddressExistsException&#40;&#41;;
    		&#125;
    
    		Role roleEveryone = roleRepository.findRole&#40;ROLE_EVERYONE&#41;;
    		Role roleCustomer = roleRepository.findRole&#40;ROLE_CUSTOMER&#41;;
    		Group groupUsers = groupRepository.findGroup&#40;GROUP_USERS&#41;;
    
    		user.addToRole&#40;roleEveryone&#41;;
    		user.addToRole&#40;roleCustomer&#41;;
    		user.addToGroup&#40;groupUsers&#41;;
    
    		userRepository.storeUser&#40;user&#41;;
    
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Creating validation ticket..."&#41;;
    		&#125;
    
    		RegistrationTicket ticket = RegistrationTicket.newTicketForUser&#40;user&#41;;
    		ticketRepository.storeTicket&#40;ticket&#41;;
    
    		String hash = ticket.getHash&#40;&#41;;
    
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Ticket created and saved with hash&#58; " + hash&#41;;
    		&#125;
    
    		// TODO&#58; USE SCHEDULER, fire and forget the mail to a scheduler
    		//       mail sending should not happen in transactional context
    		try &#123;
    
    			MimeMessage mm = mailSender.createMimeMessage&#40;&#41;;
    			MimeMessageHelper msg = new MimeMessageHelper&#40;mm, "UTF-8"&#41;;
    			msg.setFrom&#40;"xxx@xxxx.com"&#41;;
    			msg.setTo&#40;user.getEmail&#40;&#41;&#41;;
    			msg.setSubject&#40;"Welcome..."&#41;;
    			msg.setText&#40;"Hallo please click here&#58; " + hash&#41;;
    			mailSender.send&#40;msg.getMimeMessage&#40;&#41;&#41;;
    
    		&#125; catch &#40;MessagingException ex&#41; &#123;
    			logger.error&#40;"Error sending mail", ex&#41;;
    			throw new RuntimeException&#40;"Could not complete request, sending email failed", ex&#41;;
    		&#125;
    
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Successfully sent confimation mail to&#58; " + user.getEmail&#40;&#41;&#41;;
    		&#125;
    
    		if &#40;logger.isInfoEnabled&#40;&#41;&#41; &#123;
    			logger.info&#40;"New registration pending for validation &#40;user=" + user.getName&#40;&#41;.toString&#40;&#41; +"/id=" + user.getId&#40;&#41; + "&#41;"&#41;;
    		&#125;
    
    		return true;
    	&#125;
    
    	public User enroll&#40;String hash&#41; &#123;
    
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Looking up hash&#58; " + hash&#41;;
    		&#125;
    
    		RegistrationTicket ticket = ticketRepository.findTicket&#40;hash&#41;;
    
    		if &#40;ticket == null || !ticket.isValid&#40;&#41;&#41; &#123;
    			throw new ConfirmRegistrationException&#40;"User already confirmed or confirmation sequence does not exist"&#41;;
    		&#125;
    
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Found valid hash&#58; " + hash&#41;;
    		&#125;
    
    		ticket.validateUser&#40;&#41;;
    		User user = ticket.getUser&#40;&#41;;
    
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"Got associated user&#58; " + user.toString&#40;&#41;&#41;;
    		&#125;
    
    		ticketRepository.removeTicket&#40;ticket&#41;;
    
    		// TODO&#58; Need programmatic transaction here or put in advisor
    		// ALSO&#58; USE SCHEDULER, fire and forget the mail to a scheduler
    		// mail sending should not happen in transactional context
    		try &#123;
    			MimeMessage mm = mailSender.createMimeMessage&#40;&#41;;
    			MimeMessageHelper msg = new MimeMessageHelper&#40;mm, "UTF-8"&#41;;
    			msg.setFrom&#40;"xxx@xxx.com"&#41;;
    			msg.setTo&#40;user.getEmail&#40;&#41;&#41;;
    			msg.setSubject&#40;"Registration complete"&#41;;
    			msg.setText&#40;"it worked Hallo please click here to login..."&#41;;
    			mailSender.send&#40;msg.getMimeMessage&#40;&#41;&#41;;
    		&#125; catch &#40;MessagingException ex&#41; &#123;
    			logger.error&#40;"Error sending mail", ex&#41;;
    			throw new ConfirmRegistrationException&#40;ex&#41;;
    		&#125;
    
    		if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
    			logger.debug&#40;"User confirmed successfully&#58; " + user.toString&#40;&#41;&#41;;
    		&#125;
    		return user;
    	&#125;
    Reg.Service is used in a controller .. i.e.:

    Code:
    public class RegistrationEnrollController extends ParameterizableViewController implements InitializingBean &#123;
    
    	// ~ Instance fields =======================================================
    
    	private IRegistrationService regService;
    
    	// ~ Methods ===============================================================
    
    	public void setRegistrationService&#40;IRegistrationService service&#41; &#123;
    		this.regService = service;
    	&#125;
    
    	public void afterPropertiesSet&#40;&#41; throws Exception &#123;
            if &#40;regService == null&#41; &#123;
                throw new IllegalArgumentException&#40;"A registration service is required"&#41;;
            &#125;
        &#125;
    
    	protected ModelAndView handleRequestInternal&#40;HttpServletRequest request, HttpServletResponse response&#41; throws Exception &#123;
    		String hash = RequestUtils.getRequiredStringParameter&#40;request, "enroll"&#41;;
    		User user = regService.enroll&#40;hash&#41;;
    		Map model = new HashMap&#40;&#41;;
    
    		if &#40;user.isEnabled&#40;&#41;&#41; &#123;
    			model.put&#40;"username", user.getName&#40;&#41;.first&#40;&#41;&#41;;
    		&#125;
    	    return new ModelAndView&#40;getViewName&#40;&#41;, model&#41;;
    	&#125;
    
    &#125;

    The strategy classes
    Code:
    /**
     * This <em>Layer Supertype</em> is an attempt to unify repository strategies.
     *
     * <p>The motivation behind this is to reduce code duplication in combination
     * with persistence strategies used in different repositories. Most
     * repositories perform CRUD operations all the time. They expose their
     * functions with <em>intetion revealing interfaces</em> that underneath
     * calling the appropriate strategies to manage persitence.</p>
     *
     * <p>However, the intention revealing interfaces are duplicated from the
     * Respositories down to the strategies resulting in seperate strategies for
     * seperate Repositories. Though the strategies are mostly doing CRUD or other
     * common operations the intent is to unify the strategy in one that works
     * for all repositories.</p>
     *
     * <p><strong>This class &#40;and implementations&#41; should be considered highly
     * experimental at this stage and must be used soly for experimentation.</strong></p>
     *
     * @author  Andreas Aderhold
     * @version $Revision$
     */
    public interface RepositoryStrategy &#123;
    
    	// cirteria should NOT be hibernate specific
    	Collection matching&#40;Criteria specs&#41;;
    	Object soleMatch&#40;Criteria specs&#41;;
    
    	void save&#40;Object object&#41; throws DataAccessException;
    	void update&#40;Object object&#41; throws DataAccessException;
    	void refresh&#40;Object object&#41; throws DataAccessException;
    
    	void evict&#40;Object object&#41;;
    
    	void remove&#40;Object role&#41; throws DataAccessException;
    
    	String getObjectId&#40;Object role&#41; throws DataAccessException;
    
    
    	void removeById&#40;Class clazz, Serializable id&#41; throws DataAccessException;
    	Object findById&#40;Class clazz, Serializable id&#41; throws DataAccessException;
    
    
    	Criteria createCriteria&#40;Class clazz&#41;;
    	
    &#125;
    
    public class HibernateRepositoryStrategy extends HibernateDaoSupport implements RepositoryStrategy &#123;
    
    	public void evict&#40;Object object&#41; &#123;
    		getHibernateTemplate&#40;&#41;.evict&#40;object&#41;;
    	&#125;
    
    	public Object findById&#40;Class clazz, Serializable id&#41; throws DataAccessException &#123;
    		return getHibernateTemplate&#40;&#41;.load&#40;clazz, id&#41;;
    	&#125;
    
    	public String getObjectId&#40;Object object&#41; throws DataAccessException &#123;
    		return getSession&#40;&#41;.getIdentifier&#40;object&#41;.toString&#40;&#41;;
    	&#125;
    
    	// cirteria that come in should NOT be hibernate specific
    	public Collection matching&#40;Criteria specs&#41; &#123;
    		return specs.list&#40;&#41;;
    	&#125;
    
    	public void refresh&#40;Object object&#41; throws DataAccessException &#123;
    		getHibernateTemplate&#40;&#41;.refresh&#40;object&#41;;
    	&#125;
    
    	public void remove&#40;Object object&#41; throws DataAccessException &#123;
    		getHibernateTemplate&#40;&#41;.delete&#40;object&#41;;
    	&#125;
    
    	public void removeById&#40;Class clazz, Serializable id&#41; throws DataAccessException &#123;
    		getHibernateTemplate&#40;&#41;.delete&#40;findById&#40;clazz, id&#41;&#41;;
    	&#125;
    
    	public void save&#40;Object object&#41; throws DataAccessException &#123;
    		getHibernateTemplate&#40;&#41;.saveOrUpdate&#40;object&#41;;
    	&#125;
    
    	public Object soleMatch&#40;Criteria specs&#41; &#123;
    		List objs = specs.list&#40;&#41;;
    		if &#40;objs.iterator&#40;&#41;.hasNext&#40;&#41;&#41;
    			return objs.iterator&#40;&#41;.next&#40;&#41;;
    
    		return null;
    	&#125;
    
    	public void update&#40;Object object&#41; throws DataAccessException &#123;
    		getHibernateTemplate&#40;&#41;.update&#40;object&#41;;
    	&#125;
    
    	public Criteria createCriteria&#40;Class clazz&#41; &#123;
    		return getSession&#40;&#41;.createCriteria&#40;clazz&#41;;
    	&#125;
    &#125;

    Hope this gives you the picture. The comments are not always proper, i'm lazy here too...

  10. #30
    Join Date
    Aug 2004
    Location
    Germany, Magdeburg
    Posts
    279

    Default

    Hope this gives you the picture. The comments are not always proper, i'm lazy here too...
    Great thanks! This was a very interesting read. Looks really smooth and well done. I understood nearly everything what's going on. Very good coding style, indeed.

    The only thing I noticed was this:

    Code:
     /**
        * The persistence strategy used for this repository
        */
       private RepositoryStrategy strategy;
    I would prefer a more descriptive name:

    Code:
     
       private RepositoryStrategy persistenceStrategy;

    But never the less, the picture is now clear. I also did some fine adds to my little iBatis research project. I declared a service interface 'UserManager' and it's implemented by a concret class called 'PostgreUserManager'.

    It turned out that the iBatis API only adds a minor amount of lines of code to the implementation. So I feel that there is no need to exclude these minor lines and define a common 'DAO' interface etc. I guess using a XML mapping file to specify the DAO related logic (SQL and mapping) works like a DAO itself. Just a quite different language than Java.

    Also I guess the Hibernate solution would require a slightly different implementation in the service object itself. So having only one service object implementing the binding for a given database directly, feels very good and straight. There isn't no replication of tests going on, like it would have been when using a DAO centered solution.

    But I will take a closer look when adding the document repository. This repository is a domain model element and has a lot of responsibilities (managing workflow informations, document history, conversion of documents etc). I think the code metrics will demand something like a persistence strategy or a real DAO or a deligate or even a split in the inheritance hierarchy or whatever.

    I will keep you informed about my findings and the turn overs.


    Cheers and thanks for the code snippets,


    Martin (Kersten)

    PS: Boy, just scroll down this page and you know we both were quite busy writing all this... . ;-)

Similar Threads

  1. Problem with HibernateInterceptor
    By prane in forum Data
    Replies: 5
    Last Post: Oct 16th, 2007, 08:01 AM
  2. Replies: 2
    Last Post: Oct 10th, 2005, 05:12 PM
  3. could not satisfy dependencies
    By springuser in forum Container
    Replies: 4
    Last Post: Apr 26th, 2005, 01:15 PM
  4. Replies: 1
    Last Post: Apr 25th, 2005, 07:37 PM
  5. Content Provider vs View Model
    By Martin Kersten in forum Swing
    Replies: 21
    Last Post: Mar 10th, 2005, 02:25 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •