Project

General

Profile

Actions

CdmLibraryConventions » History » Revision 8

« Previous | Revision 8/26 (diff) | Next »
Andreas Kohlbecker, 07/07/2009 01:53 PM


The following still is a draft summary excepts - currently under discussion


Cdm Library Conventions

Persistence

The persistence layer is meant to be mainly used by experienced cdm core developers, it provides data access methods for the service layer.

In order to keep this layer clear and easy to maintain a small amount of flexible methods having many parameters is preferred over having many methods with less parameters but which are easy to use for uninitiated and less experienced cdm developers. The service layer in contrast will offer more methods to make it easy to start developing cdm applications.

Template for methods might look like:

  • getting a list of instances:

public List<T> list(Class<? extends T> clazz,... args, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)

Service Layer

From my point of view we should remove all redundant methods like many of the getAllSomething() etc. as well as we should harmonize the method names like e.g. renaming like findByUuid(UUID uuid) to just load(UUID uuid) This cleaning procedure would not only affect the service layer but also the persistence layer were we have the > same redundancy.

Template for methods might look like:

  • loading a cdm instance:

get(UUID uuid) and load(UUID uuid, List<String> propertyPaths)

  • searching for instances:

findBy''Something''(.. args, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)

  • getting a list of instances:

public List<T> list(Class<? extends T> clazz,... args, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)

  • paging a list of instances:

public Pager<T> page(Class<? extends T> clazz,... args, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths, Pager<T> pager)

!OrderHint

is an object containing two two field,s one for the propertyName, the other to define the sortOrder (type is enumeration OrderHint. SortOrder). CdmEntityDaoBase.list(Integer limit, Integer start, List orderHints) evaluates the orderHints and supports path like propertyNames which include *-to-one properties like createdBy.username or authorTeam.persistentTitleCache.

propertyPaths:

Allows more fine grained initialization not only of the root bean identified by its uuid but also of specific paths of the object graph. The sub graph to initialize may be defined in the propertyPaths parameter as list of paths all starting at the root bean.

You can use wildcards * LOAD_2ONE_2MANY_WILDCARD and $ LOAD_2ONE_WILDCARD for initializing all *ToOne and all *ToMany relations of a bean. NOTE: A wildcard subsequently terminates its property path.

Maybe propertyPaths should be renamed into initStrategy?

There is a bit of a conflict between the persistence layer (which has a DAO per object-hierarchy) and the service layer (which has a service-per-package, sort of). Although it's nice to have a small number of services, I think it makes sense to have services for classes like FeatureTree and DescriptionElement instead of embedding them in DescriptionService, and TermVocabulary instead of embedding it in TermService, as it means (a) you can't extend ServiceBase and get all of the logic therin for free - you have to re-implement save, find, saveAll etc for these classes, and (b) you end up with awkward method names like saveFeatureTree() to distinguish from save() etc.



=== Unstructured Excerpts===

On Pagers

Furthermore I stumbled over some inflexibility of the current Pager.

  1. It would be nice if the MAX_PAGE_LABELS could be set by a client application using service layer methods.

  2. Currently for client applications it is not possible to tell a service method using a custom pager.

To solve both issues I’d like to suggest introducing another method parameter for the service layer browse() method:

public Pager<T> page(Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, Pager<T> pager)

This would of course mean to also add an additional setRecords(List records) method to Pager (rename to IPager ?) and to modify the AbstractPagerImpl accordingly. AbstractPagerImpl would lose List records from its constructors.

Ok. That sounds like a good idea. I was customizing the pagers like this in CATE:

public Pager<T> list(Integer pageNumber, Integer pageSize) {
                Long numberOfEntities = dao.count();
                List<T> entities = dao.list(pageNumber,pageSize);
                return new StringLabelPagerImpl<T>(pageNumber, numberOfEntities,pageSize,entities) {
                        @Override
                        protected String getLabel(Integer i) {
                                T t = dao.list(i,1).get(0);
                                return t.getTitleCache();
                        }
                };
       }

(this uses the titleCache attribute to generate labels). I don't think you could do this by supplying a pager instance from outside of the method, as dao.list needs to be called in a transactional context. Perhaps we could add the following to the Pager interface:

public void generateLabels(ICdmEntityDao<T> dao);

which generates the labels, optionally using the dao to retrieve objects which lie outside the current page and thus are not in "results" (this is better, I think, than having a "setDao" method which might then expose the DAO for abuse in the controller layer once the method executes).

This would make the "browse" method look something like

public Pager<T> browse(Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, Pager<T> pager) {
    Integer numberOfResults = dao.count();
    List<T> results = new ArrayList<T>();

    if(numberOfResults > 0) { // no point checking again
      Integer start = pageSize == null ? 0 : pageSize * (pageNumber - 1);
      results = dao.list(pageSize, start, orderHints);
      pager.setResults(results);
      pager.generateLabels(dao);
    }

    return pager;
}

I've been meaning to ask: Are the pageSize / pageNumber calculations supposed to be in the service layer - is everyone happy that the arguments for list(int,int) should be "limit" and "offset". I don't think this is consistently applied across the DAO's - really there should be one pattern, otherwise it will get confusing. I remember discussing this in Berlin at the beginning of the year but not the conclusion.


Updated by Andreas Kohlbecker about 15 years ago · 8 revisions