Hi Vincent,
It's good to have you back :)

On 11/13/2012 08:15 AM, Vincent Massol wrote:
> Hi Caleb,
> 
> Thanks for taking the time to provide this feedback!
> 
> See below.
> 
> On Sep 27, 2012, at 9:52 AM, Caleb James DeLisle <[email protected]> 
> wrote:
> 
>> Hi,
>> I took a look at the new model proposal and there are a few questions and 
>> suggested changes.
>>
>>
>>
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/Entity.java#L32
>> Should this be a String rather than a Java UUID type?
> 
> Right now it's a String. I don't recall why I proposed String rather than 
> UUID, maybe to allow for changing the implementation in the future.
> 
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/Entity.java#L42
>> If we use getChildren() rather than getChildReferences(), we will be 
>> mangling the model with the database code which violates the MVC principle.
> 
> I don't agree with this. MVC has nothing to do here. In MVC, the M stands for 
> Model and this is the only thing we're talking about here. V (for View) and C 
> (for Controller) are outside of the scope.
> 
> What you hint at (I think) is a Layered Architecture and this we're going to 
> do, by having the persistence layer in a different layer. However business 
> objects are completely ok to call the persistence layer.
> 
> My idea is to use a Rich data model

This is not maintainable in the long term.

> (as the current model BTW),

Which is why we're here...

> i.e. business operations are *in* the model object interfaces.

Aka the controller is mixed with the model.

> This is where we need to agree. I personally don't want to use an Anemic 
> model where you have business objects be just data holders (ie. only 
> getters/setters) and move all business operations into a service layer.

What if you wanted to deserialize a bunch of documents in an XAR, add an object 
to each one of them and then reserialize them?
I tried to do this at one point and found out that the only two options were 
manipulating the XML or bringing up an entire wiki because XWikiDocument 
depends on everything.

Another case is when storage changes so radically that the storage API needs to 
change. Imagine for that JDBC becomes able to do asynchronous load and store 
with a callback.
In order to take advantage of this improvement we would need to find all of the 
places where code synchronously calls saveDocument() and replace them with code 
that understands callbacks.
This isn't too bad if all of the controller code is severable from the model 
but if the model calls into the store and it offers store APIs to it's 
consumers (basically everything depends on the model), something as simple as 
implementing a (radically) new store will land us back here talking about model 
3.0.

Even just making a change to the store implementation will break this model, 
how do I load a document from one store and save it to another if the document 
is aware of it's store?

Taken another way, the store has to depend on the model because it handles 
these objects in order to persist and load them, to make the model include APIs 
which depend on the store creates a cyclical dependency loop, the basic 
building block of spaghetti code.

> 
> For more on this topic:
> * http://en.wikipedia.org/wiki/Anemic_domain_model (lists pros and cons)
> * http://www.martinfowler.com/bliki/AnemicDomainModel.html
> 

I took the time to read this all over again very carefully and with a clear 
mind.
I have come to the same conclusion about Fowler's arguments as before but I 
have also noticed that he does not support your idea of mixing Document and 
storage logic.

Quoted from your martinfowler.com link:
"It's also worth emphasizing that putting behavior into the domain objects 
should not contradict the solid approach of using layering to separate domain 
logic from such things as persistence and presentation responsibilities. The 
logic that should be in a domain object is domain logic - validations, 
calculations, business rules - whatever you like to call it."



Now for completeness, although this is off the topic of the model, I will 
address the flaws in Fowler's argument.
His most prominent point is "it's not really object oriented" which is an 
obvious case of http://en.wikipedia.org/wiki/Law_of_the_instrument
"Give a small boy a hammer, and he will find that everything he encounters 
needs pounding." having once been a small boy with a hammer, the quote still 
makes me giggle..
The point is that sometimes objects make huge sense, other times procedures, 
sometimes functional programming works best, sometimes a combination of the 3 
or something else entirely. The only "one true way" is to use the right tool 
for the job.

Aside from the "only true way" argument, Fowler doesn't offer much to back his 
claim that lean objects are bad.
"The primary cost is the awkwardness of mapping to a database, which typically 
results in a whole layer of O/R mapping"
I suppose this was written before Hibernate/DN/Spring/etc existed making this 
trivial so it wasn't wrong when he wrote it.

Now aside from the zealotry rubbish and the ORM mapping issue which nolonger 
exists, I don't see any reasons that lean objects are bad and finally I reject 
the word "anemic" as a weasel word in this context as it is clearly a 
distraction from the issues.


Now back to the model:

The wiki article you linked to makes a few good points about the value of 
adding code to your objects. Validation and functions like clone() make perfect 
sense inside of an object.
What makes no sense is having references beyond the object's scope to things 
like serializers, name resolvers and storage.
I think a good rule is that I should be able to create a model object using new 
Document() and have it fully functional without the need for any other part of 
the wiki.


>> Also we run the risk of encouraging users to walk the tree looking for the 
>> entity it wants rather than creating a reference to it and using that.
>> Walking the tree would obviously incur enormous unnecessary database load.
> 
> That's the reason for the Iterator:
> EntityIterator<Entity> getChildren(EntityType type)
> 
> With an Iterator the implementation can do things like:
> * get a single item at a time from the DB
> * get a bunch of N items at once (in one request) from the DB
> 
> Now you have a valid point that we may also want to have the ability to get 
> children references. It's a good question. Now if we imagine that parents are 
> stored as RDBMS column in a document table. Getting all children documents 
> will mean doing a select in the document table where parent = a given parent 
> ref ("select parent from document doc where doc.parent = ?"). Now getting 
> getChildren() would do: select * from document doc where doc.parent = ?. The 
> extra cost would just be the loading of marshalling/unmarshalling of data 
> (which you would need thereafter in your logic). But I agree and it's 
> possible that on other stores, it could cost less to get references.
> 
> So I'd propose to have 2 methods: one for getting only children references 
> and one for getting all children entities.

Of course this can't be implemented without the document containing a reference 
to the store, back to the above issue.

> 
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/ModelContext.java
>> Do we want to continue with the concept of the global variable representing 
>> the "current" document?
> 
> There's no global variable. It's a thread local variable (i.e. visible only 
> for the current thread).
> 
> Yes I'd like to continue this. We didn't have it before in oldcore and 
> XWikiContext was passed around everywhere which is why we moved to 
> ThreadLocal.

It's a global variable which points to a thread local.
Even though only that thread can see it, the entire codebase can access it so 
from a maintainability standpoint, it's a global.

> 
>> I'm currently not brimming with alternative ideas but the heavy reliance on 
>> the XWikiContext has lead to
>> a lot of inter-modular communication which makes oldcore difficult to 
>> maintain, perhaps we can prevent
>> this in the new model.
> 
> oldcore doesn't use this since it uses XWikiContext and not the Execution 
> Context.
> 
> Maybe you could explain in more details what you mean by "lot of 
> inter-modular communication"?

It's hard to define but I can give an example:
Module A puts a few things into the context and module B, C, and D take them 
out and use them.
Now you want to remove A and you refactor everything which explicitly depends 
on it and pull it out
and everything explodes because there are hidden dependencies via other modules 
expecting things to be
placed in the context. As long as your context is a Map<String, Object> there 
will always be this
possibility. Dependency injection cleverly avoids this issue by using a 
Map<Class, Object> where you have
to have a reference to the class before you can have an object of that class so 
by controlling access to
the class, you control how far the implementations leak out.

> 
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/Object.java
>> I would rather this have a name which doesn't collide with a java.lang 
>> class. When I see Object in a .java
>> file I would like it to always mean java.lang.Object.
>>
>>
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/ObjectDefinition.java
>> I like XClass better for this purpose, it is essentially a class and 
>> creating a new name which nobody
>> has ever heard of will just steepen the learning curve and make things hard 
>> on newcomers.
> 
> This has been discussed and voted on another thread so these names will be 
> modified to match the result of that vote.

ok

> 
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/ObjectProperty.java
>> Why not give the user direct access to the property without the intervening 
>> wrapper?
> 
> Yes that's possible. However there could other methods such as a method to 
> get the property definition.
> 
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/Persistable.java#L34
>> I don't like this.
>> A Persistable is saved in a store by a user. Who is the user and where are 
>> they saving it?
> 
> The user who started the session.

Another global var which will lead to more "saveAsAuthor()" and 
"saveWithProgrammingRights()" etc. which swap around the global variable around 
before and after doing the operation.

> 
>> What if I want to have multiple stores?
> 
> Then set the store to use when you start the session.

This global var will lead to more try/finally traps where the storage is 
swapped around for one operation.

> 
>> Must there be a global variable representing the store which is implied by 
>> this function?
>> The comment and minorEdit fields are also a bit dubious, perhaps in the 
>> future they will make no sense, perhaps
>> they should really be attributes of the Document, also the "attributes" 
>> parameter reminds me of this:
>> http://geek-and-poke.com/2012/06/how-to-create-a-stable-api.html
> 
> Yep, that was exactly the reason for the attributes parameter ;)

The fact that you're adding a ffu map to a function indicates that it should be 
a function of the store, not of the model.
That way if the store is changed the parameters can change.

> 
> We can tune this API and remove comment/minorEdit if we want. I'm not sure 
> about putting them in Document; it's strange from an API POV IMO. I wouldn't 
> want to use such as API where you have to remember to set the comment in the 
> Document before calling save...

We can wrap the generic thingstore with a documentstore class/interface which 
takes precisely the parameters we want and prepares the document for storage.

> 
>> Finally, giving the document awareness of where and how it is stored 
>> violates the MVC boundries.
> 
> Don't agree, see above. Nothing to do with MVC IMO.
> 
>> I think we should not worry about the API which stores the Persistable when 
>> we are creating the model, the
>> store API should be separate so that it can evolve while the model stays 
>> stable.
> 
> See above, anemic vs rich model.

And Fowler said mixing model with storage is not what he means..

> 
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/Session.java
>> A session is essentially a transaction? If so why not call it Transaction?
> 
> A Session is form a user POV. Technically it will use a Transaction but 
> that's a technical implementation and it's more than that. It contains a 
> user; it can contain the store to use, etc.

Ok, that's valid.

> 
>> Also why does it extend Persistable?
>> How do you persist a session?
> 
> Saving a Session means saving all entities that have been modified and have 
> not been saved so far.
> This is the same as JCR (a lot of stuff come from JCR btw):
> http://www.day.com/maven/javax.jcr/javadocs/jcr-2.0/javax/jcr/Session.html
> 
> Note that JCR 2.0 has deprecated Item.save() and I've researched this a bit 
> but couldn't find the reason beyond "they wanted to simplify the API" and 
> it's causing problems to users who want to just persist a given Item and 
> what's below and the not the whole Session. Which is why I've kept having 
> Entities implement Persistable so far.

Hmm if session is going to be aware of all things which have been altered by it 
then it must provide them.
If I get a document from the Server then call save() on the session and that 
document is saved, that is two apparently unrelated objects having an effect on 
each other which is nonsensical.
I'm not sure how much use there will be for that but one approach is to pass 
the session to the save functions and this would communicate the user to save 
as and the store to save in, it would however tie your hands a little bit.

> 
>> https://github.com/xwiki/xwiki-platform/blob/feature-newmodel/xwiki-platform-core/xwiki-platform-model/xwiki-platform-model-api/src/main/java/org/xwiki/model/Space.java#L41
>> Why does addSpace() not take a Space parameter? it makes sense to me that 
>> you would do:
>> parent = storeApi.get(wikiRef, "SomeSpace", user);
>> child = new DefaultSpace();
>> child.addDocument("ADocument", new DefaultDocument("This is content."));
>> parent.addSpace("ChildSpace", child);
>> storeApi.save(parent, user);
> 
> I prefer the current API:
> 
> Entity parent = …;
> 
> Space newSpace = parent.addSpace("child space");
> Document newDocument = newSpace.addDocument("a document");
> newDocument.setContent("This is content");
> 
> // Save the new entities created below parent
> parent.save(…);
> 
> // Note: if we wanted to save all entities modified(not just the one in this 
> tree) we would write:
> session.save(…);
> 
>> I'm just brainstorming here but this seems like a reasonable approach..
> 
> Your solution doesn't hide the implementation (DefaultSpace() and 
> DefaultDocument() in this case) and makes it a lot harder to unit test. We 
> don't want to repeat the issue we have today with unit testing 
> XWiki/XWikiDocument...

What issue is this? Usually the issue I run in to is I have to bring up almost 
a whole wiki just to instantiate an XWikiDocument.
Anyway I'm sure there are other ways which respect the API consumer's right to 
use their own implementation of Space or Document.

> 
>> I noticed the lack of a User data type, is that just something which is 
>> planned for the future or is it
>> intentionally missing?
> 
> Good question. I don't know yet. A possibility is to pass the user in the 
> save() call. Another is to use the Entity's author.

+1 for passing the user to any call which needs permissions checking.


My last question is how do you differentiate this from the oldcore model?
In order to make the case that we need to have a new model, we need to prove 
that in 8 years this isn't going to be the same as the oldcore is now.


Thanks,
Caleb


> 
> Note that we also need a new User module anyway.
> 
> Thanks
> -Vincent
> 
> _______________________________________________
> devs mailing list
> [email protected]
> http://lists.xwiki.org/mailman/listinfo/devs
> 


_______________________________________________
devs mailing list
[email protected]
http://lists.xwiki.org/mailman/listinfo/devs

Reply via email to